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路径:

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
ModelForm校验

 

 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         #
ModelSubject类

 

 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())
stark组件增删改页面显示

 

 

还有一个小功能,我们的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>
base-HTML(include标签方法)

 

增加页面:

 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>
add-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>
编辑页面-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>
删除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 }
css样式

 

 

我们最核心的组件就是在一个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的属性值就是调用的接口
apps.py文件

然后就是我们的各个其他的App里面要使用我们自定义的组件的就在指定文件名的文件里面写即可

 

1 class BookConfig(ModelSubject):
2      list_display = ['id', 'title']
3     list_display_links = ['title']
4 site.register(Book)
App使用自定义组件

 

我们来整理一下逻辑:我们先看一下我们的admin里面的源码实现逻辑,我们的admin里面就只有一个admin.site的url,然后它就可以实现我们所有已经注册表格的增删改查功能,我们要使用同样的方法也是一句话就实现我们的所有表格的增删改查功能,所以url的配置是最关键的一环,没有它一切都是徒劳,

我们在admin的源码里面可以看到它定义了一个类然后用这个类实例化出来的对象命名为site,它调用了一个resister方法,就可以达到注册表格的效果,我们也先这样写,然后把源码里面的register的核心代码copy过来,再把与之相关联的url的生成代码逻辑拿过来用,然后这个实例化出来的对象就形成了,再把延展性的功能都搭建起来,就可以一步一步走下去了, 

 

posted @ 2018-03-15 22:18  dream-子皿  阅读(132)  评论(0)    收藏  举报