Sansa组件

诉求

  仿照admin组件,实现对表的URL分配管理。

实现思路

  1.在settings.py文件中注册APP,注册示例为:

'app01.apps.App01Config',
'app02.apps.App02Config',
‘stark_demo.apps.StarkDemoConfig',

  2. 在每一个APP中的apps.py 文件中添加

1 from django.apps import AppConfig
2 from django.utils.module_loading import autodiscover_modules
3 
4 class App01Config(AppConfig):
5     name = 'app01'
6 
7     def ready(self):
8         autodiscover_modules('stark',)

实现在Django项目启动时,扫描每个APP项目下的stark.py文件的文件,执行其中的代码,注册每个APP下的model,为每一个model生成增删改查四条URL。

来一张流程图进行说明。

Sansa组件_第1张图片

Starksite类和Modelstark类的创建

  1 from django.conf.urls import url
  2 from django.shortcuts import HttpResponse, render, reverse,redirect
  3 from django.utils.safestring import mark_safe
  4 from django.forms import ModelForm
  5 from app01.models import *
  6 from app02.models import *
  7 
  8 # 针对使用stark组件的默认样式类
  9 
 10 
 11 class Modelstark(object):
 12     list_display = ["__str__"]
 13     list_display_link = ["__str__"]
 14     model_form = None
 15     # init 方法在实例化类的时候执行
 16 
 17     def __init__(self, model, site):
 18         self.model = model
 19         self.site = site
 20         self.label_model = (self.model._meta.app_label, self.model._meta.model_name)
 21     '''
 22     功能:
 23         将显示、编辑、删除、增加页面的url 封装到函数中。
 24     函数名:
 25         def get_add_url(self)
 26         def get_del_url(self,obj)
 27         def get_edit_url(self,obj)
 28         def get_list_url(self)
 29     '''
 30     def get_add_url(self):
 31         add_url = reverse("%s_%s_add" % self.label_model, )
 32         return add_url
 33 
 34     def get_del_url(self,obj):
 35         del_url = reverse("%s_%s_del" % self.label_model, args=(obj.pk,))
 36         return del_url
 37 
 38     def get_edit_url(self,obj):
 39         edit_url = reverse("%s_%s_edit"% self.label_model,args=(obj.pk,))
 40         return edit_url
 41 
 42     def get_list_url(self):
 43         list_url = reverse("%s_%s_show"%self.label_model,)
 44         return list_url
 45     '''
 46     功能:
 47         为显示页面右侧的添加按钮生成动态的URL
 48     函数名:
 49         def add_btn(self,obj=None,header=False)
 50     '''
 51     def add_btn(self,obj=None,header=False):
 52         if header:
 53             pass
 54         return mark_safe("添加"%self.get_add_url())
 55     '''
 56     功能:
 57         为显示页面生成编辑、删除和复选框的标签
 58     函数名:
 59         def edit(self,obj=None,header=False)
 60         def delete(self,obj=None,header=False)
 61          def checkbox(self,obj=None,header=False)
 62     '''
 63     def edit(self,obj=None,header=False):
 64         if header:
 65             return "操作"
 66         return mark_safe("编辑"%self.get_edit_url(obj))
 67 
 68     def delete(self,obj=None,header=False):
 69         if header:
 70             return "操作"
 71         return mark_safe("删除" %self.get_del_url(obj))
 72 
 73     def checkbox(self,obj=None,header=False):
 74         if header:
 75             return mark_safe("")
 76         pass
 77 
 78         return mark_safe(""%obj.pk)
 79 
 80         pass
 81     '''
 82     功能:
 83         为每一个类生成modelform,用于渲染标签
 84         判断用户是否定义了model_form,如果定义了则用用户自己的类,没有则用默认的modelForm
 85         备注:
 86             显示中文错误提示信息的解决思想是:
 87                 在样式类中定义一个 model_form = None 的静态变量,在 def default_modelform(self) 函数中判断 model_form = None的bool值,若为False,
 88                 则表明用户没有自定义 model_form ,因此走默认的modelForm.
 89                 若model_form 为True,则表明用户对要注册的表定义了modelForm,因此用户会在自己的 XXXConfig类中声明 model_form = 自定义的modelForm,
 90                 此时用用户自定义的 modelForm
 91     函数名:
 92         def mdelform(self)
 93     '''
 94     def default_modelform(self):
 95         class modelForm(ModelForm):
 96             class Meta:
 97                 model = self.model
 98                 fields = "__all__"
 99         if not self.model_form:
100             return modelForm
101         else:
102             return self.model_form
103     '''
104        功能:
105             显示、编辑、增加、删除4个视图函数
106        函数名:
107             def show_list(self, request)
108             def add_list(self, request)
109             def edit_list(self, request,id)
110              def del_list(self, request,id)
111        '''
112     def show_list(self, request):
113         data_list = self.model.objects.all()
114         # 显示表头名称
115         header_list = []
116         for i in self.real_list_display():
117             if callable(i):
118                 # 获取函数名:函数名.__name__
119                 # header_list.append(i.__name__) -----> low版
120                 header_list.append(i(self, header=True))
121             else:
122                 if i == "__str__":
123                     header_list.append(self.model._meta.model_name.upper())
124                 else:
125                     header_list.append(self.model._meta.get_field(i).verbose_name)
126 
127         # print(self.list_display)
128         # 首先循环每一条记录
129         """
130         需要构建的样式为:
131         [
132             ["id1","name1"],
133             ["id2","name2"],
134             ["id3","name3"],
135         ]
136         """
137         # 显示数据
138         ret_data_list = []
139         for obj in data_list:
140             tmp_list = []
141             # 遍历显示的字段,i 指的是字段名称
142             for i in self.real_list_display():
143                 if callable(i):
144                     val = i(self, obj)
145                 else:
146                     # val 为反射后的value
147                     val = getattr(obj, i)
148                     # 判断当前的 字段名称 是否在 list_display_link 中,在的话让该字段对应的值作为a标签
149                     if i in self.list_display_link:
150                         val = mark_safe("%s"%(self.get_edit_url(obj),val))
151                 tmp_list.append(val)
152             ret_data_list.append(tmp_list)
153         # 显示添加按钮
154         btn_list = []
155         # 使用类.函数 的方式需要传self,但是使用类的对象.方法的方式不需要传self
156         val = Modelstark.add_btn(self)
157         btn_list.append(val)
158 
159         return render(request, "show_list.html", locals())
160 
161     def add_list(self, request):
162         modelForm = self.default_modelform()
163         errors = {}
164         if request.method == "POST":
165             form = modelForm(request.POST)
166             if form.is_valid():
167                 form.save()
168                 return redirect(self.get_list_url())
169             else:
170                 return render(request,"add.html",locals())
171         form = modelForm()
172         return render(request,"add.html",locals())
173 
174     def edit_list(self, request,id):
175         obj = self.model.objects.filter(id=id).first()
176         modelForm = self.default_modelform()
177         if request.method == "POST":
178             form = modelForm(request.POST,instance=obj)
179             if form.is_valid():
180                 form.save()
181                 return redirect(self.get_list_url())
182             else:
183                 pass
184         form = modelForm(instance=obj)
185         return render(request,"edit.html",locals())
186 
187     def del_list(self, request,id):
188         if request.method == "POST":
189             self.model.objects.filter(id=id).delete()
190             return redirect(self.get_list_url())
191         list_url = self.get_list_url()
192         return render(request,"delete.html",locals())
193     '''
194     功能:
195         在显示页面要真正显示的字段,对list_display进行扩展
196     函数名:
197         def real_list_display(self)
198     '''
199     def real_list_display(self):
200         new_list_display = []
201         new_list_display.extend(self.list_display)
202         new_list_display.append(Modelstark.edit)
203         new_list_display.append(Modelstark.delete)
204         new_list_display.insert(0,Modelstark.checkbox)
205         return new_list_display
206     '''
207     功能:
208         为没一个注册的model生成增删改查四条URL
209     函数名:
210         def get_url_func(self)
211     '''
212     def get_url_func(self):
213         tmp = []
214         app_name = self.model._meta.app_label
215         model_name = self.model._meta.model_name
216         ret = (app_name, model_name)
217         tmp.append(url("^$", self.show_list, name="%s_%s_show" %ret))
218         tmp.append(url("^add/$", self.add_list, name="%s_%s_add" %ret))
219         tmp.append(url("^edit/(\d+)/$", self.edit_list, name="%s_%s_edit" %ret))
220         tmp.append(url("^del/(\d+)/$", self.del_list, name="%s_%s_del" %ret))
221         '''
222         在访问URL之前就已经分别为每张表创建了4条URL
223         '''
224         return tmp
225 
226 
227 # 创建stark类,实例化Starksite,用于注册model
228 
229 
230 
231 class Starksite(object):
232 
233     def __init__(self):
234         # 保存表的注册信息,保存的格式为:以model类为键,以该model实例化样式对象为值
235         self._registry = {}
236 
237     # 类的注册函数
238     def register(self, model, modelconfig=None):
239         '''
240         实现model的注册
241         :param model: 要注册的类
242         :param modelconfig: 该注册类的样式对象
243         :return: None
244         '''
245         
246         '''
247         判断用户是否自己定义了样式类,如果没有,此时的modelconfig为None,则用默认的ModelStark样式类进行实例化注册;
248         若用户自定义了注册类的样式对象,则使用用于自定的样式对象
249         '''
250         if not modelconfig:
251             modelconfig = Modelstark
252         # model是每次循环注册的表,self是实例化的Startksite类的实例化对象
253         '''
254         在对注册的model进行样式实例化的时候,modelconfig类实际上是Modelstark类的实例化,实例化要执行类的__init__函数,init函数的定义为:
255             def __init__(self, model, site):
256                 self.model = model
257                 self.site = site
258                 self.label_model = (self.model._meta.app_label, self.model._meta.model_name)
259             此时实例化的方式为:类名.__init__() 方式,因此需要传self。
260             另外需要注意的是modelconfig(model,self)中的self是哪个类的实例化对象
261             当在注册类的时候没有传入制定的样式类,那么这里的self是Modelstark类的实例化,
262             但是,当用户传入定制的样式类时,此时的self是modelconfig类的实例化
263         '''
264         self._registry[model] = modelconfig(model, self)
265 
266     def get_urls(self):   
267         '''
268         生成表的一级和二级URL
269         :return: tmp 
270         '''
271         tmp = []
272         '''
273         item():使字典中的键值对以元组的形式显示
274         model: 注册的model类
275         model_config:该类对应的样式类对象
276         app_label:以字符串形式获取类所在的APP
277         model_name:以字符串的获取类名
278         '''
279         for model, model_config in self._registry.items():
280             app_label = model._meta.app_label
281             model_name = model._meta.model_name
282             #  为什么不能去掉None
283             u = url("^%s/%s/" % (app_label, model_name), (model_config.get_url_func(), None, None))
284             tmp.append(u)
285         return tmp
286     '''
287     把urls函数做成静态方法,urls.py 中执行urls函数不需要加()
288     '''
289     @property
290     def urls(self):
291         return self.get_urls(), None, None
292 
293 
294 # 创建单例对象,在每一个app中的stark文件中调用
295 site = Starksite()

各功能的实现思路

一级与二级URL的设计思路

  在注册完表后,执行urls.py文件,执行

1 urlpatterns = [
2     url(r'^admin/', admin.site.urls),
3     url(r'^stark/', site.urls),
4 ]

  site为类Starksite的单例实例化对象,site.urls 调用类下的urls方法,而urls方法实际上是执行get_urls(self) 方法,为每一个model首先生成一级URL,其次调用类的样式对象下的get_url_func(self)

函数,生成二级URL,同时为每一个增删改查URL创建别名,用于反向解析。

显示页面的数据显示

  1.数据显示的思路

    首先类中定义一个 list_display = ["__str__"]的静态变量,此静态变量在用于没有重写该变量时,则默认显示该对象的 def __str__(self) 方法的返回值。 

    将显示的数据以 [ [1,"python",12,"编辑"],[2,"java",23,"编辑"],[3,"php",111,"编辑"],]的形式在后后台处理成功后,循环判断要显示的数据是一个字符串还是可执行函数,若是可执行函数则执行函数后追加保存到列表中,若是字符串则直接追加保存在列表中。

    PS:

     1.getattr(obj,str) 函数用于反射,实现通过对象的字段名名称找到该字段对应的值。

     2.要循环的字段实际上是new_list_display, 即对list_display进行扩展,是默认显示的字段有复选框、对象名称、编辑、删除

    def real_list_display(self):
        new_list_display = []
        new_list_display.extend(self.list_display)
        new_list_display.append(Modelstark.edit)
        new_list_display.append(Modelstark.delete)
        new_list_display.insert(0,Modelstark.checkbox)
        return new_list_display

 

  2.编辑和删除a标签的实现

    把编辑和删除的a标签分别作为一个函数,使该函数的返回值为 mark_safe("编辑"),在循环要显示的字段对象的数据时,执行该函数。

    PS:

      mark_safe()函数实现前端safe相同的功能,使django不转移标签对象。

  3.表头的实现

    根据list_display 循环显示表头,当遇到“__str__”时,找到该该对象的“__str__“”方法的返回值,当遇到制定的字段名称,如public时,则使用 self.model._meta.get_field(i).verbose_name获取该字段的verbose_name,追加到列表中。

    PS:

     get_field(arg) 可以获取到model表中该arg字段对象,该对象可以调用字段的属性,如verbose_name。

  4.让字段作为a标签,实现跳转到编辑页面的实现

    在循环显示数据的时候, 判断通过getattr()方法获取的字段值是否在list_display_link中,如果在,则使用mark_safe()使该value成为a标签。

  5. ModelForm 组件实现页面的增删改查

    只需要为要进行form渲染的类继承ModelForm,即可。

  6.使用ModelForm实现显示中文错误信息

     在样式类中创建一个model_form = None的静态变量,在创建 ModelForm 类的函数 def default_modelform(self)中判断model_form的值,值为None,则使用默认的modelform,如果为True,则使用用户stark.py中定义的样式类。

在每个APP中stark.py文件的创建

 1 from .models import *
 2 # 对单例对象的调用
 3 from stark_demo.service.sites import site,Modelstark
 4 from django.forms import ModelForm
 5 
 6 
 7 class BookModelForm(ModelForm):
 8     '''
 9     重写model的modelform类
10     '''
11     class Meta:
12         model = Book
13         fields = "__all__"
14         error_messages = {
15             "title":{"required":"不能为空"},
16             "public":{"required":"不能为空"}
17         }
18 
19 
20 class BookConfig(Modelstark):
21 
22     list_display = ["id","title","public"]
23     list_display_link = ["id","title",]
24     model_form = BookModelForm
25 
26 
27 site.register(Book,BookConfig)

 

你可能感兴趣的:(Sansa组件)