django-admin效果仿写,stark 自定义组件之增删改
如下代码都是在同一个类ModelSubject里面写的封装好的方法,
首先是铺垫:
1 # 获取展示页面的url 2 def get_stand_url(self): 3 stand_url = reverse('%s_standlist' % self.namespace) 4 return stand_url 5 6 # 获取编辑页面的url 7 def get_edit_url(self, obj): 8 edit_url = reverse('%s_edit' % self.namespace, args=(obj.pk,)) 9 return edit_url 10 11 # 获取删除页面的url 12 def get_dele_url(self, obj): 13 dele_url = reverse('%s_dele' % self.namespace, args=(obj.pk,)) 14 return dele_url 15 16 # 获取增加页面的url 17 def get_add_url(self): 18 add_url = reverse('%s_add' % self.namespace) 19 return add_url
然后是url路径:
url(r'^stark/', site.urls) # 这一句几乎没有改动过
1 # ModelForm校验添加和编辑页面 2 def get_modelform_class(self): 3 from django.forms import widgets 4 5 class AllModelForm(ModelForm): 6 class Meta: 7 model = self.model 8 fields = '__all__' 9 if not self.model_form_class: # 这里的model_form_class在上面被定义了默认是None, 10 # 我们的分发下去的App里面自定义的file文件里面注册model类的时候实例化出来的对象,在注册的时候传过来的这个变量 11 return AllModelForm 12 else: 13 return self.model_form_class
1 class ModelSubject(object): 2 """ 3 我们在这里模拟admin源码里面的ModelAdmin, 4 """ 5 list_display = ["__str__"] # 我们在这里给空列表里面加上"__str__",它就相当于是一个默认值, 6 model_form_class = None # 为下面我们判断用户是否有自定义ModelForm校验方式而做铺垫 7 list_display_links = [] # 为我们后面用户是否有自定义可跳转字段做铺垫 8 # 就像我们的函数里面有默认值的参数一样,如果有传参就使用我们的自定义传参,如果没有传参就使用我们默认的参数也就是这个字符串, 9 # 这里是为了给我们后面的代码做铺垫,我们的目的是在我们的数据展示页面里面默认就会把复选框和编辑还有删除按钮都加上, 10 # 在这里把空列表里面添加上一个默认的字符串,是为了我们后面往该列表里面添加默认固定数据也就是复选框和编辑删除按钮做铺垫 11 12 # 静态内置方法 13 def __init__(self, model, site): 14 self.model = model # 当我们生成一个实例化对象的时候需要把model这个参数传进来, 15 # 必须要传,它是位置参数,然后我们所传入的那个model就是我们在models.py里面定义的每一个表名也就是类名 16 self.site = site 17 self.namespace = '{}_{}'.format(self.model._meta.app_label, self.model._meta.model_name) 18 #
1 # ModelForm校验数据添加页面 2 def add_view(self, request): 3 FormClass = self.get_modelform_class() 4 if request.method == 'GET': 5 form = FormClass() 6 return render(request, 'file/add.html', {'form': form}) 7 else: 8 data_list = FormClass(data=request.POST) 9 if data_list.is_valid(): 10 data_list.save() 11 return redirect(self.get_stand_url()) 12 else: 13 return render(request, 'file/add.html', {'form': data_list}) 14 15 # ModelForm校验数据编辑页面 16 def edit_view(self, request, id): 17 edit_list = self.model.objects.filter(pk=id).first() 18 FormClass = self.get_modelform_class() 19 if request.method == 'GET': 20 data_list = FormClass(instance=edit_list) 21 return render(request, 'file/edit.html', {'form': data_list}) 22 else: 23 data_list = FormClass(data=request.POST, instance=edit_list) 24 if data_list.is_valid(): 25 data_list.save() 26 return redirect(self.get_stand_url()) 27 else: 28 return render(request, 'file/edit.html', {'form': data_list}) 29 30 # 数据删除页面 31 def dele_view(self, request, id): 32 del_obj = self.model.objects.filter(pk=id).first() 33 if request.method == 'GET': 34 stand_url = self.get_stand_url() 35 return render(request, 'file/dele.html', {'del_obj': del_obj, 'list_url': stand_url}) 36 else: 37 del_obj.delete() 38 39 return redirect(self.get_stand_url())
还有一个小功能,我们的admin里面可以让用户自定义显示的表格字段,那些字段可以变成a标签包裹着的能够跳转的字段样式,这里我们只需要很简短的几行代码即可实现:
# 展示页面默认显示按钮被存放的列表 def get_list_display(self): new_li = [] new_li.extend(self.list_display) # 我们这里的extend是把它后面的列表里面的数据都取出来放到我们自己的这个列表里面来 if not self.list_display_links: # 这里的list_display_links就是我们上面在类里面定义的静态属性的值,在这里被调用,要两处相呼应上,否则会报错 new_li.append(ModelSubject.edit) # &&& 跟如下同样特征的代码块相呼应我们在这里使用类名去调用函数名,得到的是一个函数的方法, # 函数如果有参数是需要我们传参数的;但是我们如果使用self去调用的话,self就是实例化出来的对象, # 而我们的对象去调用函数方法的时候就不需要去传自己了也就是self, new_li.append(ModelSubject.dele) new_li.insert(0, ModelSubject.checkbox) # 把checkbox放到第一个位置,使用insert插入到索引为0
还有在展示的时候要做一点点处理:
1 # 展示页面 2 def stand_li(self, request): 3 # print(self.model) 4 # 所以我们在这里可以获取到当前的url里面的表名,然后直接使用orm语句即可得到当前表格的所有信息 5 6 # 生成表头数据 7 # ['id','title','price',edit] 8 header_list = [] 9 for field in self.get_list_display(): 10 if callable(field): 11 ret = field(self, is_header=True) 12 header_list.append(ret) 13 else: 14 if field == '__str__': # 这里是判断我们的list_display列表里面是否有我们自定义传入的值,如果没有的话, 15 # 就是直接等于我们在静态属性里面设定的那个默认的'__str__',也就是说如果这一步判断成立, 16 # 那么就证明我们的用户没有自定义展示的字段,我们就需要自己给浏览器一个字段去展示,那个字段就是我们这里所设定的那个大写的表名 17 header_list.append(self.model._meta.model_name.upper()) # 我们这里的操作是 18 else: 19 obj = self.model._meta.get_field(field) # 我们的list_display里面是一个个的字符串, 20 # 把字符串放到get_field里面来可以把我们的字符串转换成类对象, 21 header_list.append(obj.verbose_name) # 我们这里的verbose_name在model里面是内置方法, 22 # 我们的verbose_name本质上是对我们的字段进行描述的,比如我们的book里面的title可以在字段约束里面设 23 # verbose_name='书名',类似于这样,把它变成中文,然后我们在前端HTML模板里面渲染的时候就可以渲染出来中文了, 24 # 而不是使用默认的英文,当然了我们如果不设置verbose_name的值那么就使用默认的title作为字段名传到浏览器 25 26 # 生成表单数据列表 27 ret = self.model.objects.all() 28 # print('self.list_display', self.list_display) 29 data_list = [] 30 for obj in ret: # 我们遍历这个queryset集合得到的obj是它的每一个对象 31 temp = [] 32 for field in self.get_list_display(): # 我们遍历list_display得到每一个字符串 33 if callable(field): 34 # res = field(obj) # @@@更上面的特殊标识的代码块相呼应 35 res = field(self, obj) # &&& 这里跟上面同样标识的代码块相呼应的,上面我们使用的类名去调用函数名, 36 # 得到的是一个函数,这里就是给所调用的函数传参的,self,和obj都是我们传给函数的参数; 37 # 如果我们使用self这个对象去调用函数名的方法的话,就不需要再传一个self作为参数进去了,我们两种方法都可以,需要对应上即可 38 else: 39 res = getattr(obj, field) # 使用getattr方法去判断该对象是否具有,field属性方法, 40 # getattr里面需要两个参数(类对象,字符串属性方法) 41 if field in self.list_display_links: # 我们这里是判断表单里面的字段是否在links表格里面被自定义作为可跳转标签, 42 # 如果答案是肯定的,那么我们就需要把a标签给拼出来 43 res = mark_safe("<a href='%s'>%s</a>" % (self.get_edit_url(obj), res)) 44 temp.append(res) 45 data_list.append(temp) 46 47 # print('data_list', data_list) 48 49 """ 50 我们最终得到的数据类型是如下格式:列表套着列表 51 [ 52 使用orm语句得到的每一个类对象,有几个表格就有几个对象 53 ] 54 list_display=['id','title',] 55 [ 56 [1,'python',<a>编辑</a>], 57 [2,'java',<a>编辑</a>], 58 ] 59 """ 60 add_url = self.get_add_url() 61 62 return render(request, 'file/hello.html', locals())
我们在其他的App程序里面要使用list_display_links就像下面这样用:
1 from file.tool.tag import site, ModelSubject 2 from .models import * 3 from django.forms import ModelForm 4 5 6 class BookConfig(ModelSubject): 7 list_display_links = ['title'] 8 site.register(Book, BookConfig)
前端模板的增删改:
我们的增加和编辑页面是几乎一样的,我们就写一个自定义标签,在各自的页面上引用一套即可
自定义标签页面(使用的include方法):
1 <div class="container"> 2 <div class="row"> 3 <div class="col-md-8"> 4 5 <form action="" method="post" novalidate> 6 {% csrf_token %} 7 {% for i in form %} 8 <div class="form-group"> 9 <label for="">{{ i.label }}</label> 10 <div class="input_style">{{ i }} 11 <span class="error">{{ i.errors.0 }}</span> 12 </div> 13 </div> 14 {% endfor %} 15 16 <div class="form-group"> 17 <p><input type="submit" class="btn btn-primary"></p> 18 </div> 19 20 </form> 21 </div> 22 </div> 23 </div>
增加页面:
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta http-equiv="X-UA-Compatible" content="IE=edge"> 6 <meta name="viewport" content="width=device-width, initial-scale=1"> 7 <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css"> 8 <link rel="stylesheet" href="/static/css/base.input.css"> 9 <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script> 10 <script src="https://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js"></script> 11 <title>Title</title> 12 </head> 13 <body> 14 <h3>添加数据</h3> 15 16 {% include 'file/base_form.html' %} 17 18 </body> 19 </html>
编辑页面:
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta http-equiv="X-UA-Compatible" content="IE=edge"> 6 <meta name="viewport" content="width=device-width, initial-scale=1"> 7 <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css"> 8 <link rel="stylesheet" href="/static/css/base.input.css"> 9 <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script> 10 <script src="https://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js"></script> 11 <title>Title</title> 12 </head> 13 <body> 14 <h3>编辑数据</h3> 15 16 {% include 'file/base_form.html' %} 17 18 </body> 19 </html>
删除页面:
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta http-equiv="X-UA-Compatible" content="IE=edge"> 6 <meta name="viewport" content="width=device-width, initial-scale=1"> 7 <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css"> 8 <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script> 9 <script src="https://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js"></script> 10 11 <title>Title</title> 12 </head> 13 <body> 14 15 <table class="table table-hover"> 16 <thead> 17 <tr> 18 <th> 19 20 </th> 21 </tr> 22 </thead> 23 <tbody> 24 <tr> 25 <td class="warning">{{ del_obj }}</td> 26 </tr> 27 </tbody> 28 </table> 29 30 <form action="" method="post"> 31 {% csrf_token %} 32 <input type="submit" class="btn btn-danger" value="确认删除?"> 33 <a href="{{ list_url }}"></a> 34 </form> 35 36 </body> 37 </html>
还有静态文件配置:
1 .form-group .input_style input{ 2 display:block; 3 width:100%; 4 height:34px; 5 padding:6px 12px; 6 font-size:14px; 7 line-height: 1.42857143; 8 color:#555; 9 background-color:#fff; 10 background-image:none; 11 border:1px solid #ccc; 12 border-radius:4px; 13 -webkit-box-shadow:inset 0 1px 1px rgba(0,0,0, .075); 14 box-shadow:inset 0 1px rgba(0,0,0, .075); 15 -webkit-transition:border-color 16 ease-in-out .15s, -webkit-box-shadow ease-in-out .15s; 17 -o-transition:border-color ease-in-out .15s, box-shadow ease-in-out .15s; 18 transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; 19 } 20 21 .error{ 22 color:red; 23 }
我们最核心的组件就是在一个django项目里面创建一个App,然后这个App里面不写views视图函数,在里面要建立一个文件夹,然后里面建立一个文件,这个文件里面就是我们的组件的核心代码块,它的功能就是仿照着我们的django-admin里面的功能去实现的,
下面就是核心代码块:
1 from django.conf.urls import url 2 from django.shortcuts import HttpResponse, render, redirect, reverse 3 from django.utils.safestring import mark_safe 4 from one.models import * 5 from two.models import * 6 from django.forms import ModelForm # 这个ModelForm里面封装了很强大的功能,要把源码过一遍 7 8 9 class ModelSubject(object): 10 """ 11 我们在这里模拟admin源码里面的ModelAdmin, 12 """ 13 list_display = ["__str__"] # 我们在这里给空列表里面加上"__str__",它就相当于是一个默认值, 14 model_form_class = None # 为下面我们判断用户是否有自定义ModelForm校验方式而做铺垫 15 list_display_links = [] # 为我们后面用户是否有自定义可跳转字段做铺垫 16 # 就像我们的函数里面有默认值的参数一样,如果有传参就使用我们的自定义传参,如果没有传参就使用我们默认的参数也就是这个字符串, 17 # 这里是为了给我们后面的代码做铺垫,我们的目的是在我们的数据展示页面里面默认就会把复选框和编辑还有删除按钮都加上, 18 # 在这里把空列表里面添加上一个默认的字符串,是为了我们后面往该列表里面添加默认固定数据也就是复选框和编辑删除按钮做铺垫 19 20 # 静态内置方法 21 def __init__(self, model, site): 22 self.model = model # 当我们生成一个实例化对象的时候需要把model这个参数传进来, 23 # 必须要传,它是位置参数,然后我们所传入的那个model就是我们在models.py里面定义的每一个表名也就是类名 24 self.site = site 25 self.namespace = '{}_{}'.format(self.model._meta.app_label, self.model._meta.model_name) 26 # self.app_model_name = (self.model._meta.app_label, self.model._meta.model_name) # 这里写得跟上面一句是一样的效果, 27 # 这里调用的时候需要有两个%s,因为这里是一个元祖,而我们上面的namespace是一个字符串,不是一个元祖,所以只需要一个%s即可,调用的时候就这点区别 28 # 我们这里的namespace是因为会频繁使用到所以就把它作为一个内置静态属性写入到这里,其他地方如果要调用它就直接使用self.namespace即可 29 # .format的方法:'{}_{}'.format(a,b) 30 31 # 获取展示页面的url 32 def get_stand_url(self): 33 stand_url = reverse('%s_standlist' % self.namespace) 34 return stand_url 35 36 # 获取编辑页面的url 37 def get_edit_url(self, obj): 38 edit_url = reverse('%s_edit' % self.namespace, args=(obj.pk,)) 39 return edit_url 40 41 # 获取删除页面的url 42 def get_dele_url(self, obj): 43 dele_url = reverse('%s_dele' % self.namespace, args=(obj.pk,)) 44 return dele_url 45 46 # 获取增加页面的url 47 def get_add_url(self): 48 add_url = reverse('%s_add' % self.namespace) 49 return add_url 50 51 # 展示页面默认附带的编辑按钮 52 def edit(self, obj=None, is_header=False): 53 if is_header: 54 return '操作' 55 return mark_safe('<a href="%s">编辑</a>' % reverse('%s_edit' % self.namespace, args=(obj.pk,))) 56 57 # 展示页面默认附带的删除按钮 58 def dele(self, obj=None, is_header=False): 59 if is_header: 60 return '删除' 61 return mark_safe("<a href='%s'>删除</a>" % reverse('%s_dele' % self.namespace, args=(obj.pk,))) 62 63 # 展示页面附带的默认复选框 64 def checkbox(self, obj=None, is_header=False): 65 if is_header: 66 return mark_safe("<input id='action-toggle' type='checkbox'>") 67 return mark_safe("<input type='checkbox' values='%s'>" % obj.pk) 68 69 # 展示页面默认显示按钮被存放的列表 70 def get_list_display(self): 71 new_li = [] 72 new_li.extend(self.list_display) # 我们这里的extend是把它后面的列表里面的数据都取出来放到我们自己的这个列表里面来 73 if not self.list_display_links: 74 new_li.append(ModelSubject.edit) # &&& 跟如下同样特征的代码块相呼应我们在这里使用类名去调用函数名,得到的是一个函数的方法, 75 # 函数如果有参数是需要我们传参数的;但是我们如果使用self去调用的话,self就是实例化出来的对象, 76 # 而我们的对象去调用函数方法的时候就不需要去传自己了也就是self, 77 new_li.append(ModelSubject.dele) 78 new_li.insert(0, ModelSubject.checkbox) # 把checkbox放到第一个位置,使用insert插入到索引为0 79 """ 80 # @@@ 跟下面特殊标识的代码块相呼应 81 new_li.append(self.edit) 82 new_li.append(self.dele) 83 new_li.insert(0,self.checkbox) 84 """ 85 return new_li 86 87 # 展示页面 88 def stand_li(self, request): 89 # print(self.model) 90 # 所以我们在这里可以获取到当前的url里面的表名,然后直接使用orm语句即可得到当前表格的所有信息 91 92 # 生成表头数据 93 # ['id','title','price',edit] 94 header_list = [] 95 for field in self.get_list_display(): 96 if callable(field): 97 ret = field(self, is_header=True) 98 header_list.append(ret) 99 else: 100 if field == '__str__': # 这里是判断我们的list_display列表里面是否有我们自定义传入的值,如果没有的话, 101 # 就是直接等于我们在静态属性里面设定的那个默认的'__str__',也就是说如果这一步判断成立, 102 # 那么就证明我们的用户没有自定义展示的字段,我们就需要自己给浏览器一个字段去展示,那个字段就是我们这里所设定的那个大写的表名 103 header_list.append(self.model._meta.model_name.upper()) # 我们这里的操作是 104 else: 105 obj = self.model._meta.get_field(field) # 我们的list_display里面是一个个的字符串, 106 # 把字符串放到get_field里面来可以把我们的字符串转换成类对象, 107 header_list.append(obj.verbose_name) # 我们这里的verbose_name在model里面是内置方法, 108 # 我们的verbose_name本质上是对我们的字段进行描述的,比如我们的book里面的title可以在字段约束里面设 109 # verbose_name='书名',类似于这样,把它变成中文,然后我们在前端HTML模板里面渲染的时候就可以渲染出来中文了, 110 # 而不是使用默认的英文,当然了我们如果不设置verbose_name的值那么就使用默认的title作为字段名传到浏览器 111 112 # 生成表单数据列表 113 ret = self.model.objects.all() 114 # print('self.list_display', self.list_display) 115 data_list = [] 116 for obj in ret: # 我们遍历这个queryset集合得到的obj是它的每一个对象 117 temp = [] 118 for field in self.get_list_display(): # 我们遍历list_display得到每一个字符串 119 if callable(field): 120 # res = field(obj) # @@@更上面的特殊标识的代码块相呼应 121 res = field(self, obj) # &&& 这里跟上面同样标识的代码块相呼应的,上面我们使用的类名去调用函数名, 122 # 得到的是一个函数,这里就是给所调用的函数传参的,self,和obj都是我们传给函数的参数; 123 # 如果我们使用self这个对象去调用函数名的方法的话,就不需要再传一个self作为参数进去了,我们两种方法都可以,需要对应上即可 124 else: 125 res = getattr(obj, field) # 使用getattr方法去判断该对象是否具有,field属性方法, 126 # getattr里面需要两个参数(类对象,字符串属性方法) 127 if field in self.list_display_links: # 我们这里是判断表单里面的字段是否在links表格里面被自定义作为可跳转标签, 128 # 如果答案是肯定的,那么我们就需要把a标签给拼出来 129 res = mark_safe("<a href='%s'>%s</a>" % (self.get_edit_url(obj), res)) 130 temp.append(res) 131 data_list.append(temp) 132 133 # print('data_list', data_list) 134 135 """ 136 我们最终得到的数据类型是如下格式:列表套着列表 137 [ 138 使用orm语句得到的每一个类对象,有几个表格就有几个对象 139 ] 140 list_display=['id','title',] 141 [ 142 [1,'python',<a>编辑</a>], 143 [2,'java',<a>编辑</a>], 144 ] 145 """ 146 add_url = self.get_add_url() 147 148 # 生成分页器 149 path = request.path_info 150 page_num = request.GET.get('page') 151 from file.utensil.page import MyPage 152 page = MyPage(page_num, len(data_list), path) 153 page_html = page.page_html() 154 return render(request, 'file/hello.html', 155 {'data_list': data_list[page.start:page.end], 156 'page_html': page_html, 'header_list': header_list, 'add_url': add_url}) 157 # return render(request, 'file/hello.html', locals()) 158 159 """ 160 元类, 161 162 我们定义了类之后,可以用这个类创建出实例对象,这里的大前提是我们要先有类才能有实例化对象, 163 那么,问题来了,我们如果想创建类呢,就需要根据metaclass来创建出类,所以,先定义metaclass,然后创建类,所以连接起来就是我们要先定义metaclass, 164 就可以创建类,最后再实例化出来对象,所以,metaclass允许你创建类或者修改类,换句话说,我们可以把类看做是metaclass创建出来的"实例", 165 166 167 这里面都是啥意思啊,都怎么用啊,对比着我们的ModelForm,这下面的代码块是把ModelForm里面的东西都写活了, 168 def _get_modelform(self): 169 labels = self.labels if self.labels else None 170 error_message = self.error_form if self.error_form else None 171 widgets = self.wdigets if self.wdigets else None 172 meta_config = type('Meta', (), { 173 'model': self.model, 174 'fields': '__all__', 175 'labels': labels, 176 'error_messages': error_message, 177 'widgets': widgets 178 }) 179 """ 180 181 # ModelForm校验添加和编辑页面 182 def get_modelform_class(self): 183 from django.forms import widgets 184 185 class AllModelForm(ModelForm): 186 class Meta: 187 model = self.model 188 fields = '__all__' 189 if not self.model_form_class: # 这里的model_form_class在上面被定义了默认是None, 190 # 我们的分发下去的App里面自定义的file文件里面注册model类的时候实例化出来的对象,在注册的时候传过来的这个变量 191 return AllModelForm 192 else: 193 return self.model_form_class 194 195 # ModelForm校验数据添加页面 196 def add_view(self, request): 197 FormClass = self.get_modelform_class() 198 if request.method == 'GET': 199 form = FormClass() 200 return render(request, 'file/add.html', {'form': form}) 201 else: 202 data_list = FormClass(data=request.POST) 203 if data_list.is_valid(): 204 data_list.save() 205 return redirect(self.get_stand_url()) 206 else: 207 return render(request, 'file/add.html', {'form': data_list}) 208 209 # ModelForm校验数据编辑页面 210 def edit_view(self, request, id): 211 edit_list = self.model.objects.filter(pk=id).first() 212 FormClass = self.get_modelform_class() 213 if request.method == 'GET': 214 data_list = FormClass(instance=edit_list) 215 return render(request, 'file/edit.html', {'form': data_list}) 216 else: 217 data_list = FormClass(data=request.POST, instance=edit_list) 218 if data_list.is_valid(): 219 data_list.save() 220 return redirect(self.get_stand_url()) 221 else: 222 return render(request, 'file/edit.html', {'form': data_list}) 223 224 # 数据删除页面 225 def dele_view(self, request, id): 226 del_obj = self.model.objects.filter(pk=id).first() 227 if request.method == 'GET': 228 stand_url = self.get_stand_url() 229 return render(request, 'file/dele.html', {'del_obj': del_obj, 'list_url': stand_url}) 230 else: 231 del_obj.delete() 232 233 return redirect(self.get_stand_url()) 234 235 # 获取url,此为第二次分发 236 def get_urls(self): 237 temp = [] 238 temp.append(url(r'^$', self.stand_li, name='%s_standlist' % self.namespace)) 239 temp.append(url(r'^(\d+)/dele/', self.dele_view, name='%s_dele' % self.namespace)) 240 temp.append(url(r'^(\d+)/edit/', self.edit_view, name='%s_edit' % self.namespace)) 241 temp.append(url(r'^add/', self.add_view, name='%s_add' % self.namespace)) 242 return temp 243 244 @property 245 def urls(self): 246 return self.get_urls() 247 248 249 class Stark(object): 250 """ 251 我们这里面的功能是可以跟上面的类写到一起去的,但是我们为了功能解耦,所以就分开写了,这里的主要功能就是 252 生成registry的字典,把键值对生成,然后我们最终的结果是要得到一个实例化对象,供我们后面的程序调用,这里的类才是主要的,核心的代码块 253 而我们上面的那个ModelSubject是辅助我们这里的功能,它之所以分发出去是为了便于扩展其他的功能,我们的自定义样式, 254 还有很多的方法和参数,就像我们的admin里面的ModelAdmin一样,长达1400多行代码,单独把它分离出去便于功能的扩展 255 """ 256 257 def __init__(self): 258 self._registry = {} # 这里是定义一个私有属性,就是为了避免被子类修改 259 260 # 注册model表 261 def register(self, model, model_config=None): # 我们是仿照着admin的源码写的组件,这里的model_config默认值是None, 262 # 我们在传参的时候,如果给它传值,那么就使用我们传入的值替换掉这个None 263 # 它的源码里面有这几个参数,我们也要按照顺序把这几个参数加进来 264 if not model_config: 265 model_config = ModelSubject # 我们这里的model_config我们上面的类ModelSubject实例化出来的对象, 266 # 它是上面的类所实例化出来的对象,这一句写得明明白白的,这大白话再看不懂就真是白学了, 267 self._registry[model] = model_config(model, self) 268 269 # 获取url,第一次分发 270 def get_urls(self): 271 li = [] 272 for model, model_config in self._registry.items(): # 我们在这里所循环的model_config就是 273 # 我们往上数第四行所实例化出来的那个model_config,它是上面的ModelSubject这个类所实例化出来对象, 274 model_name = model._meta.model_name # 这里的._meta.model_name是获取字符串格式的类名, 275 app_label = model._meta.app_label # 这里的._meta.app_labe获取的是字符串格式的App名,都是为了跟url做匹配, 276 sli = url(r'%s%s/' % (app_label, model_name), (model_config.urls, None, None)) # 我们这里的model_config, 277 # 它后面的.urls是在调用一个私有方法,我们的私有方法就是使用.urls来调用,不用加上括号, 278 # 因为有@property这个装饰器在里面起到的作用,然后我们需要找到model_config这个实例对象是哪个类生成的, 279 # 然后找到该类所拥有的方法,从里面找到urls,届时,那个urls就是我们在这里调用的那个urls了, 280 # 所以关键的点就是我们的model_config,老师讲课的时候一再地强调过这个model_config从何而来,这个是关键, 281 li.append(sli) 282 return li 283 284 # 我们最终的数据结构就是这样的,嵌套多层 285 # [ 286 # url( 287 # r'',( 288 # [ 289 # (url(r'',views.add)), 290 # (url(r'',views.edit)), 291 # ], 292 # none,none) 293 # ) 294 # ] 295 @property 296 def urls(self): 297 return self.get_urls(), None, None 298 299 300 site = Stark()
我们的组件App里面有一个apps.py文件
1 from django.apps import AppConfig 2 from django.utils.module_loading import autodiscover_modules 3 4 5 class FileConfig(AppConfig): 6 name = 'file' 7 8 def ready(self): 9 autodiscover_modules('file') # 这里是自动扫描的意思,系统会自动把所有名字带file的文件都加载出来, 10 # 扫描即加载的意思,这个名字就是随意起的,但是前提是你必须要对应上才可以,文件名和文件夹的名字,我们要想使用我们自己写的组件就需要在所有要使用它的App里面建立一个文件,文件名要是我们上面的类里面的静态属性name的值才可以,否则都不能用这个name的属性值就是调用的接口
然后就是我们的各个其他的App里面要使用我们自定义的组件的就在指定文件名的文件里面写即可
1 class BookConfig(ModelSubject): 2 list_display = ['id', 'title'] 3 list_display_links = ['title'] 4 site.register(Book)
我们来整理一下逻辑:我们先看一下我们的admin里面的源码实现逻辑,我们的admin里面就只有一个admin.site的url,然后它就可以实现我们所有已经注册表格的增删改查功能,我们要使用同样的方法也是一句话就实现我们的所有表格的增删改查功能,所以url的配置是最关键的一环,没有它一切都是徒劳,
我们在admin的源码里面可以看到它定义了一个类然后用这个类实例化出来的对象命名为site,它调用了一个resister方法,就可以达到注册表格的效果,我们也先这样写,然后把源码里面的register的核心代码copy过来,再把与之相关联的url的生成代码逻辑拿过来用,然后这个实例化出来的对象就形成了,再把延展性的功能都搭建起来,就可以一步一步走下去了,

浙公网安备 33010602011771号