django-admin 仿写stark组件action,filter筛选过滤,search查询

Posted 孟郊

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了django-admin 仿写stark组件action,filter筛选过滤,search查询相关的知识,希望对你有一定的参考价值。

 

写在StandLi里面的方法都是通过ModelSubject这个类里面的stand_li方法,在它里面用StandLi这个类实例化出来一个对象,这个实例化出来的对象把数据传给前端html模板进行渲染,所以StandLi这个类里面的方法如果你找不到就是在给前端传数据用的

tag.py

技术分享图片
  1 from django.conf.urls import url
  2 from django.shortcuts import render, redirect, reverse
  3 from django.utils.safestring import mark_safe
  4 from django.forms import ModelForm  # 这个ModelForm里面封装了很强大的功能,要把源码过一遍
  5 from django.db.models import ForeignKey, ManyToManyField
  6 from django.utils.http import urlencode
  7 import copy
  8 
  9 
 10 # 这个类主要帮我们处理多级过滤的a标签,我们之前是把多级过滤的a标签给写到get_filter_link_tags这个函数里面,
 11 # 后来为了实现功能解耦,避免单个函数代码量过大,就把这个功能封装成了一个类,以便于阅读,以及功能扩展
 12 class LinkTagsGen(object):
 13     def __init__(self, data, filter_field, request):
 14         self.data = data
 15         self.filter_field = filter_field
 16         self.request = request
 17 
 18     def __iter__(self):
 19         """
 20         所有的可迭代对象内部都是实现了__iter__方法,我们把数据写到这里就是实现的数据的可迭代
 21         :return: yield 多级过滤的A标签
 22         """
 23         current_id = int(self.request.GET.get(self.filter_field.filter_name, 0))
 24         params = copy.deepcopy(self.request.GET)
 25         params._mutable = True
 26         if params.get(self.filter_field.filter_name):
 27             del params[self.filter_field.filter_name]
 28             _url = "%s?%s" % (self.request.path_info, params.urlencode())
 29             yield mark_safe("<a href=‘%s‘>All</a>" % _url)
 30         else:
 31             yield mark_safe("<a href=‘#‘ class=‘active‘>All</a>")
 32 
 33         for item in self.data:  # self.data是一个个的queryset集合以及元祖,((1,‘已出版‘),(2,‘未出版‘)),
 34             # <QuerySet[<Publish:人民出版社>,<Publish:北京出版社>]>
 35             pk, text = None, None
 36             if self.filter_field.filter_obj.choices:
 37                 pk, text = item[0], item[1]
 38             elif isinstance(self.filter_field.filter_obj, ForeignKey) or isinstance(self.filter_field.filter_obj, ManyToManyField):
 39                 pk, text = item.pk, item
 40 
 41             params[self.filter_field.filter_name] = pk
 42             _url = "%s?%s" % (self.request.path_info, params.urlencode())
 43             if current_id == pk:
 44                 link_tag = "<a class=‘active‘ href=‘%s‘>%s</a>" %(_url, text)
 45             else:
 46                 link_tag = "<a href=‘%s‘>%s</a>" % (_url, text)
 47             yield mark_safe(link_tag)
 48 
 49 
 50 # 为每一个过滤的字段封装成整体类
 51 class FilterField(object):
 52     def __init__(self, filter_name, filter_obj):
 53         self.filter_name = filter_name
 54         self.filter_obj = filter_obj
 55 
 56     def get_data(self):
 57         if isinstance(self.filter_obj, ForeignKey) or isinstance(self.filter_obj, ManyToManyField):
 58             return self.filter_obj.rel.to.objects.all()
 59         elif self.filter_obj.choices:
 60             return self.filter_obj.choices
 61 
 62 
 63 # 服务于ModelSubject下面的stand_li,我们把stand_li里面的很多方法给封装到这个类里面实现功能解耦,
 64 # 主要是为了减轻我们的StandLi里面的代码量
 65 class StandLi(object):
 66     def __init__(self, config, request, queryset):
 67         """
 68         :param config: 它就是我们下面的类ModelSubject所传过来的它的self实例对象,
 69         我们在这里要使用那些方法和变量就需要把它的这个实例对象拿过来,否则如下搬过来的代码块都会失效
 70         :param request: 我们这个类是在下面的ModelSubject里面调用然后在那里实例化出来的对象,所以这个request是它传过来的
 71         :param queryset: 同上,这个queryset也是ModelSubject所传过来的参数,供下面的代码调用
 72         """
 73         self.config = config
 74         self.request = request
 75         self.queryset = queryset
 76 
 77         # 生成分页器
 78         path = self.request.path_info
 79         params = self.request.GET
 80         page_num = request.GET.get(page, 1)  # 如果没有找到page,就返回1,也就是第一页
 81         from file.utensil.page import MyPage
 82         count = queryset.count()
 83         page = MyPage(page_num, count, path, params)
 84         self.pagination = page
 85         data_list = self.queryset[page.start:page.end]
 86         self.data_list = data_list
 87         # page_html = page.page_html()  # 这里我们可以把page_html方法直接在前端HTML模板里面引用
 88 
 89         # actions 实现批量操作功能
 90         self.actions = self.config.get_actions()  # [patch_init, patch_delete]
 91         # print(‘actions‘, self.actions)
 92 
 93         # filter 实现筛选功能
 94         self.list_filter = self.config.list_filter
 95 
 96     # 实现多级过滤的类里面封装的一个函数
 97     def get_filter_link_tags(self):
 98         for filter_name in self.list_filter:
 99             filter_obj = self.config.model._meta.get_field(filter_name)
100             filter_field = FilterField(filter_name, filter_obj)
101             # print("filter_field", filter_field.get_data())
102             """
103             filter_field <QuerySet [<Publish: 长春出版社>, <Publish: 香港中文大学出版社>, <Publish: 中信出版社>]>
104             filter_field ((0, ‘已出版‘), (1, ‘未出版‘))
105             filter_field <QuerySet [<Writer: White>, <Writer: Black>, <Writer: Miss Lin>]>
106             """
107             val = LinkTagsGen(filter_field.get_data(), filter_field, self.request)
108             yield val
109 
110     # 这里是使用两个yield去实现的多级过滤
111     #  展示筛选条件
112     # def get_filter_link_tags(self):  # list_filter=[‘state‘,‘publish‘,‘authors‘]
113     #
114     #     for filter_name in self.list_filter:
115     #         current_id = int(self.request.GET.get(filter_name, 0))  # 这里加上int之后我们点击超链接标签的时候就会有字体颜色的变化
116     #         # current_id = self.request.GET.get(filter_name, 0)  # 这里没有加上int点击超链接的时候不会有变化
117     #         # print(‘current_id‘, current_id)
118     #         filter_obj = self.config.model._meta.get_field(filter_name)
119     #         # print(‘filter_obj‘, filter_obj)
120     #         filter_field = FilterField(filter_name, filter_obj)
121     #         # print("filter_field",filter_field)
122     #         def inner(filter_field, current_id):
123     #             # print(filter_field.get_data())
124     #
125     #             # 这里得出的结果是我们的多对多字段和一对多字段的所有关联数据
126     #             # <QuerySet [<Publish: 长春出版社>, <Publish: 香港中文大学出版社>, <Publish: 中信出版社>]>
127     #             # <QuerySet [<Writer: White>, <Writer: Black>, <Writer: Miss Lin>]>
128     #
129     #             for obj in filter_field.get_data():
130     #                 if isinstance(filter_field.filter_obj, ForeignKey) or \
131     #                         isinstance(filter_field.filter_obj, ManyToManyField):
132     #                     # link_tags=[]
133     #                     params = copy.deepcopy(self.request.GET)
134     #                     params._mutable = True
135     #                     params[filter_field.filter_name] = obj.pk
136     #
137     #                     if current_id == obj.pk:
138     #                         yield mark_safe("<a class=‘active‘ href=‘?%s‘>%s</a>" % (params.urlencode(), obj))
139     #                     else:
140     #                         yield mark_safe("<a href=‘?%s‘>%s</a>" % (params.urlencode(), obj))
141     #                 elif filter_field.filter_obj.choices:
142     #                     params = copy.deepcopy(self.request.GET)
143     #                     params._mutable = True
144     #                     params[filter_field.filter_name] = obj[0]
145     #                     if current_id == obj[0]:
146     #                         yield mark_safe("<a class=‘active‘ href=‘?%s‘>%s</a>" % (params.urlencode(), obj[1]))
147     #                     else:
148     #                         yield mark_safe("<a href=‘?%s‘>%s</a>" % (params.urlencode(), obj[1]))
149     #                 else:
150     #                     # params = copy.deepcopy(self.request.GET)
151     #                     # params._mutable = True
152     #                     # params[filter_field.filter_name] = obj
153     #                     # yield mark_safe("<a href=‘?%s‘>%s</a>" % (params.urlencode(), obj))
154     #                     ...
155     #         yield inner(filter_field, current_id)
156 
157     # 把自定义的action内容放到一个列表里面,以键值对的数据类型
158     def handle_actions(self):
159         temp = []
160         for action_func in self.actions:
161             temp.append({name: action_func.__name__, "desc": action_func.desc})
162         return temp
163 
164     # 获取表头数据
165     def get_header(self):
166         # 生成表头数据
167         # [‘id‘,‘title‘,‘price‘,edit]
168         header_list = []
169         for field in self.config.get_list_display():
170             if callable(field):
171                 ret = field(self, is_header=True)
172                 header_list.append(ret)
173             else:
174                 if field == __str__:  # 这里是判断我们的list_display列表里面是否有我们自定义传入的值,如果没有的话,
175                     # 就是直接等于我们在静态属性里面设定的那个默认的‘__str__‘,也就是说如果这一步判断成立,
176                     # 那么就证明我们的用户没有自定义展示的字段,我们就需要自己给浏览器一个字段去展示,那个字段就是我们这里所设定的那个大写的表名
177                     header_list.append(self.config.model._meta.model_name.upper())  # 我们这里的操作是
178                 else:
179                     obj = self.config.model._meta.get_field(field)  # 我们的list_display里面是一个个的字符串,
180                     # 把字符串放到get_field里面来可以把我们的字符串转换成类对象,
181                     header_list.append(obj.verbose_name)  # 我们这里的verbose_name在model里面是内置方法,
182                     # 我们的verbose_name本质上是对我们的字段进行描述的,比如我们的book里面的title可以在字段约束里面设
183                     # verbose_name=‘书名‘,类似于这样,把它变成中文,然后我们在前端HTML模板里面渲染的时候就可以渲染出来中文了,
184                     # 而不是使用默认的英文,当然了我们如果不设置verbose_name的值那么就使用默认的title作为字段名传到浏览器
185 
186         return header_list
187 
188     # 获取表单数据
189     def get_body(self):
190         # 生成表单数据列表(我们把这个函数挪到上面这个类里面来之后,把循环遍历的数据改动了,之前是把当前表格的数据取出来之后就直接遍历它,
191         # 后来我们有了搜索功能,那么就不能遍历表格里面的所有数据了,需要把我们过滤查询出来的数据给遍历出来
192         # ret = self.config.model.objects.all().count()
193         # print(‘self.list_display‘, self.list_display)
194         data_list = []
195         for obj in self.data_list:  # 我们遍历这个queryset集合得到的obj是它的每一个对象
196             temp = []
197             for field in self.config.get_list_display():  # 我们遍历list_display得到每一个字符串
198                 if callable(field):
199                     # res = field(obj)  # @@@更上面的特殊标识的代码块相呼应
200                     res = field(self.config, obj)  # &&& 这里跟上面同样标识的代码块相呼应的,上面我们使用的类名去调用函数名,
201                     # 得到的是一个函数,这里就是给所调用的函数传参的,self,和obj都是我们传给函数的参数;
202                     # 如果我们使用self这个对象去调用函数名的方法的话,就不需要再传一个self作为参数进去了,我们两种方法都可以,需要对应上即可
203                 else:
204                     res = getattr(obj, field)  # 使用getattr方法去判断该对象是否具有,field属性方法,
205                     # getattr里面需要两个参数(类对象,字符串属性方法)
206                     if field in self.config.list_display_links:  # 我们这里是判断表单里面的字段是否在links表格里面被自定义作为可跳转标签,
207                         # 如果答案是肯定的,那么我们就需要把a标签给拼出来
208                         res = self.config.get_link_tag(obj, res)
209                 temp.append(res)
210             data_list.append(temp)
211 
212         # print(‘data_list‘, data_list)
213 
214         """
215         我们最终得到的数据类型是如下格式:列表套着列表
216         [
217         使用orm语句得到的每一个类对象,有几个表格就有几个对象
218         ]
219         list_display=[‘id‘,‘title‘,]
220         [
221         [1,‘python‘,<a>编辑</a>],
222         [2,‘java‘,<a>编辑</a>],
223         ]
224         """
225         return data_list
226 
227 
228 class ModelSubject(object):
229     """
230     我们在这里模拟admin源码里面的ModelAdmin,
231     """
232     list_display = ["__str__"]  # 我们在这里给空列表里面加上"__str__",它就相当于是一个默认值,
233     model_form_class = None  # 为下面我们判断用户是否有自定义ModelForm校验方式而做铺垫
234     search_fields = []
235     list_display_links = []  # 为我们后面用户是否有自定义可跳转字段做铺垫
236     # 就像我们的函数里面有默认值的参数一样,如果有传参就使用我们的自定义传参,如果没有传参就使用我们默认的参数也就是这个字符串,
237     # 这里是为了给我们后面的代码做铺垫,我们的目的是在我们的数据展示页面里面默认就会把复选框和编辑还有删除按钮都加上,
238     # 在这里把空列表里面添加上一个默认的字符串,是为了我们后面往该列表里面添加默认固定数据也就是复选框和编辑删除按钮做铺垫
239     actions = []
240     list_filter = []  # 多级过滤
241 
242     # 静态内置方法
243     def __init__(self, model, site):
244         self.model = model  # 当我们生成一个实例化对象的时候需要把model这个参数传进来,
245         # 必须要传,它是位置参数,然后我们所传入的那个model就是我们在models.py里面定义的每一个表名也就是类名
246         self.site = site
247         self.namespace = {}_{}.format(self.model._meta.app_label, self.model._meta.model_name)
248         # self.app_model_name = (self.model._meta.app_label, self.model._meta.model_name)  # 这里写得跟上面一句是一样的效果,
249         # 这里调用的时候需要有两个%s,因为这里是一个元祖,而我们上面的namespace是一个字符串,不是一个元祖,所以只需要一个%s即可,调用的时候就这点区别
250     # 我们这里的namespace是因为会频繁使用到所以就把它作为一个内置静态属性写入到这里,其他地方如果要调用它就直接使用self.namespace即可
251     # .format的方法:‘{}_{}‘.format(a,b)
252 
253     # 默认actions批量删除
254     def patch_delete(self, queryset):
255         queryset.delete()
256         return None
257     patch_delete.desc = 批量删除
258 
259     # 获取真正展示的actions
260     def get_actions(self):
261         temp = []
262         temp.extend(self.actions)  # [patch_init,patch_delete]
263         temp.append(ModelSubject.patch_delete)
264         return temp
265 
266     # 获取展示页面的url
267     def get_stand_url(self):
268         stand_url = reverse(%s_standlist % self.namespace)
269         return stand_url
270 
271     # 获取编辑页面的url
272     def get_edit_url(self, obj):
273         edit_url = reverse(%s_edit % self.namespace, args=(obj.pk,))
274         return edit_url
275 
276     # 获取删除页面的url
277     def get_dele_url(self, obj):
278         dele_url = reverse(%s_dele % self.namespace, args=(obj.pk,))
279         return dele_url
280 
281     # 获取增加页面的url
282     def get_add_url(self):
283         add_url = reverse(%s_add % self.namespace)
284         return add_url
285 
286     # 展示页面默认附带的编辑按钮
287     def edit(self, obj=None, is_header=False):
288         if is_header:
289             return 操作
290         return mark_safe(<a href="%s">编辑</a> % reverse(%s_edit % self.namespace, args=(obj.pk,)))
291 
292     # 展示页面默认附带的删除按钮
293     def dele(self, obj=None, is_header=False):
294         if is_header:
295             return 删除
296         return mark_safe("<a href=‘%s‘>删除</a>" % reverse(%s_dele % self.namespace, args=(obj.pk,)))
297 
298     # 展示页面附带的默认复选框
299     def checkbox(self, obj=None, is_header=False):
300         if is_header:
301             return mark_safe("<input id=‘action-toggle‘ type=‘checkbox‘>")
302         return mark_safe("<input type=‘checkbox‘ value=‘%s‘ name=‘_selected_action‘>" % obj.pk)
303 
304     # 展示页面默认显示按钮被存放的列表
305     def get_list_display(self):
306         new_li = []
307         new_li.extend(self.list_display)  # 我们这里的extend是把它后面的列表里面的数据都取出来放到我们自己的这个列表里面来
308         if not self.list_display_links:
309             new_li.append(ModelSubject.edit)  # &&& 跟如下同样特征的代码块相呼应我们在这里使用类名去调用函数名,得到的是一个函数的方法,
310         # 函数如果有参数是需要我们传参数的;但是我们如果使用self去调用的话,self就是实例化出来的对象,
311         # 而我们的对象去调用函数方法的时候就不需要去传自己了也就是self,
312         new_li.append(ModelSubject.dele)
313         new_li.insert(0, ModelSubject.checkbox)  # 把checkbox放到第一个位置,使用insert插入到索引为0
314         """
315         # @@@ 跟下面特殊标识的代码块相呼应
316         new_li.append(self.edit)
317         new_li.append(self.dele)
318         new_li.insert(0,self.checkbox)
319         """
320         return new_li
321 
322     # 模糊查询
323     def get_search_condition(self):
324         from django.db.models import Q
325         search_condition = Q()
326         search_condition.connector = or
327         if self.search_fields:  # 如果用户有自定义的查询字段,我们就走这个if下面的代码,
328             key_word = self.request.GET.get(q)  # 取出用户输入的input里面的值,
329             if key_word:
330                 for search_field in self.search_fields:  # 遍历用户自定义的查询字段列表,
331                     search_condition.children.append((search_field + "__contains", key_word))
332         return search_condition
333 
334     # 处理用户自定义的link超链接字段标签,然后让超链接携带url键值对参数方法,供get_body使用
335     def get_link_tag(self, obj, val):
336         params = self.request.GET
337         params = copy.deepcopy(params)
338         params._mutable = True
339         # from django.http import QueryDict
340         # qqx = QueryDict(mutable=True)
341         qqx = {}  # 这里只写一行,等效于上面的两行,当然了前提是我们要引入urlencoded
342         qqx[list_filter] = params.urlencode()
343         whh = mark_safe("<a href=‘{}?{}‘>{}</a>".format(self.get_edit_url(obj), urlencode(qqx), val))
344         return whh
345 
346     # 获取多级过滤的数据
347     def get_filter_condition(self):
348         from django.db.models import Q
349         fiter_condition = Q()
350         for field, val in self.request.GET.items():
351             if field in self.list_filter:
352                 fiter_condition.children.append((field, val))
353         return fiter_condition
354 
355     # 展示页面
356     def stand_li(self, request):
357         # print(self.model)
358         # 所以我们在这里可以获取到当前的url里面的表名,然后直接使用orm语句即可得到当前表格的所有信息
359         if request.method == POST:
360             # print(‘request.POST.get‘, request.POST.get(‘action‘))
361             # print(‘request.POST.getlist‘, request.POST.getlist("_selected_action"))
362             # 打印出来的结果是request.POST.getlist [‘on‘, ‘on‘] 这个on是从何而来的,我的input里面的所有value值都设定的是obj.pk,
363             # 这个obj.pk是有值的,
364             # 为什么这里打印出来的getlist是两个on?我们的input标签上面有name值,
365             # 然后我们使用request.POST.get后面的括号里面放的是name属性的值,然后它的返回值是我们的input标签里面的value值,
366             # 都说了是value值,怎么还能加上s呢?简直愚蠢,
367 
368             pk_list = request.POST.getlist("_selected_action")
369             queryset = self.model.objects.filter(pk__in=pk_list)
370             func_name = request.POST.get("action")
371             func = getattr(self, func_name)
372             ret = func(queryset)
373 
374         self.request = request
375 
376         # 关于search的模糊查询
377         search_condition = self.get_search_condition()
378 
379         # action
380         # a=self.model.objects.all().count()  # 这样就是可以获取我们的queryset集合的总数据长度,
381         # 然后就可以用它去传给我们的分页组件,用它也可以,直接用count就能获取数据长度,或者是用len也行,我之前都是用len获取的
382 
383         # filter多级过滤,
384         get_filter_condition = self.get_filter_condition()
385         queryset = self.model.objects.filter(search_condition).filter(get_filter_condition)
386         add_url = self.get_add_url()
387         sl = StandLi(self, request, queryset)  # 这里是把我们的StandLi这个类所需要的参数都传给它,然后通过StandLi实例化出来的一个对象
388         # 然后在这里实例化出来一个对象我在这里调用那个对象就能够使用那个类里面的封装的方法了
389 
390         return render(request, file/hello.html, locals())
391 
392     # ModelForm校验添加和编辑页面
393     def get_modelform_class(self):
394         from django.forms import widgets
395 
396         class AllModelForm(ModelForm):
397             class Meta:
398                 model = self.model
399                 fields = __all__
400         if not self.model_form_class:  # 这里的model_form_class在上面被定义了默认是None,
401             # 我们的分发下去的App里面自定义的file文件里面注册model类的时候实例化出来的对象,在注册的时候传过来的这个变量
402             return AllModelForm
403         else:
404             return self.model_form_class
405 
406     # ModelForm校验数据添加页面
407     def add_view(self, request):
408         FormClass = self.get_modelform_class()
409         if request.method == GET:
410             form = FormClass()
411             return render(request, file/add.html, {form: form})
412         else:
413             data_list = FormClass(data=request.POST)
414             if data_list.is_valid():
415                 data_list.save()
416                 return redirect(self.get_stand_url())
417             else:
418                 return render(request, file/add.html, {form: data_list})
419 
420     # ModelForm校验数据编辑页面
421     def edit_view(self, request, id):
422         edit_list = self.model.objects.filter(pk=id).first()
423         FormClass = self.get_modelform_class()
424         if request.method == GET:
425             data_list = FormClass(instance=edit_list)
426             return render(request, file/edit.html, {form: data_list})
427         else:
428             data_list = FormClass(data=request.POST, instance=edit_list)
429         if data_list.is_valid():
430             data_list.save()
431             return redirect(self.get_stand_url())
432         else:
433             return render(request, file/edit.html, {form: data_list})
434 
435     # 数据删除页面
436     def dele_view(self, request, id):
437         del_obj = self.model.objects.filter(pk=id).first()
438         if request.method == GET:
439             stand_url = self.get_stand_url()
440             return render(request, file/dele.html, {del_obj: del_obj, list_url: stand_url})
441         else:
442             del_obj.delete()
443 
444             return redirect(self.get_stand_url())
445 
446     # 获取url,此为第二次分发
447     def get_urls(self):
448         temp = []
449         temp.append(url(r^$, self.stand_li, name=%s_standlist % self.namespace))
450         temp.append(url(r^(\d+)/dele/, self.dele_view, name=%s_dele % self.namespace))
451         temp.append(url(r^(\d+)/edit/, self.edit_view, name=%s_edit % self.namespace))
452         temp.append(url(r^add/, self.add_view, name=%s_add % self.namespace))
453         return temp
454 
455     @property
456     def urls(self):
457         return self.get_urls()
458 
459 
460 class Stark(object):
461     """
462     我们这里面的功能是可以跟上面的类写到一起去的,但是我们为了功能解耦,所以就分开写了,这里的主要功能就是
463     生成registry的字典,把键值对生成,然后我们最终的结果是要得到一个实例化对象,供我们后面的程序调用,这里的类才是主要的,核心的代码块
464     而我们上面的那个ModelSubject是辅助我们这里的功能,它之所以分发出去是为了便于扩展其他的功能,我们的自定义样式,
465     还有很多的方法和参数,就像我们的admin里面的ModelAdmin一样,长达1400多行代码,单独把它分离出去便于功能的扩展
466     """
467 
468     def __init__(self):
469         self._registry = {}  # 这里是定义一个私有属性,就是为了避免被子类修改
470 
471     # 注册model表
472     def register(self, model, model_config=None):  # 我们是仿照着admin的源码写的组件,这里的model_config默认值是None,
473         # 我们在传参的时候,如果给它传值,那么就使用我们传入的值替换掉这个None
474         # 它的源码里面有这几个参数,我们也要按照顺序把这几个参数加进来
475         if not model_config:
476             model_config = ModelSubject  # 我们这里的model_config我们上面的类ModelSubject实例化出来的对象,
477             # 它是上面的类所实例化出来的对象,这一句写得明明白白的,这大白话再看不懂就真是白学了,
478         self._registry[model] = model_config(model, self)
479 
480     # 获取url,第一次分发
481     def get_urls(self):
482         li = []
483         for model, model_config in self._registry.items():  # 我们在这里所循环的model_config就是
484             # 我们往上数第四行所实例化出来的那个model_config,它是上面的ModelSubject这个类所实例化出来对象,
485             model_name = model._meta.model_name  # 这里的._meta.model_name是获取字符串格式的类名,
486             app_label = model._meta.app_label  # 这里的._meta.app_labe获取的是字符串格式的App名,都是为了跟url做匹配,
487             sli = url(r%s%s/ % (app_label, model_name), (model_config.urls, None, None))  # 我们这里的model_config,
488             # 它后面的.urls是在调用一个私有方法,我们的私有方法就是使用.urls来调用,不用加上括号,
489             # 因为有@property这个装饰器在里面起到的作用,然后我们需要找到model_config这个实例对象是哪个类生成的,
490             # 然后找到该类所拥有的方法,从里面找到urls,届时,那个urls就是我们在这里调用的那个urls了,
491             # 所以关键的点就是我们的model_config,老师讲课的时候一再地强调过这个model_config从何而来,这个是关键,
492             li.append(sli)
493         return li
494 
495     # 我们最终的数据结构就是这样的,嵌套多层
496     # [
497     # url(
498     # r‘‘,(
499     # [
500     # (url(r‘‘,views.add)),
501     # (url(r‘‘,views.edit)),
502     # ],
503     # none,none)
504     # )
505     # ]
506     @property
507     def urls(self):
508         return self.get_urls(), None, None
509 
510 
511 site = Stark()
tag.py

 

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="/static/plugins/bs/css/bootstrap.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     <title>Title</title>
 11 
 12     <style>
 13         .filter a {
 14             padding: 3px 6px;
 15             border: 1px solid #336699;
 16             color: white;
 17             background-color: pink;
 18         }
 19 
 20         .active {
 21             background-color: #336699 !important;
 22             color: white !important;
 23         }
 24     </style>
 25 
 26 </head>
 27 <body>
 28 <div class="jumbotron">
 29     <div class="container">
 30         <h1>数据展示</h1>
 31         <h2>code change the world</h2>
 32     </div>
 33 </div>
 34 
 35 <div class="container">
 36     <div class="row">
 37         <div class="col-md-8">
 38 
 39             <a href="{{ add_url }}">
 40                 <button class="btn btn-primary" value="添加">添加数据</button>
 41             </a>
 42             {% if sl.config.search_fields %}
 43                 <div class="pull-right form-group">
 44 
 45                     <form action="" method="get" class="form-inline">
 46                         <input type="text" class="form-control" name="q" value="">
 47                         <input type="submit" class="btn btn-info" value="search">
 48                     </form>
 49 
 50                 </div>
 51             {% endif %}
 52 
 53             <form action="" method="post">
 54                 {% csrf_token %}
 55                 <div>
 56                     <select class="form-control" name="action" id="" style="width:200px;margin:8px 2px;
 57                     display:inline-block;vertical-align:-1px">
 58                         <option value="">---------</option>
 59                         {% for item in sl.handle_actions %}
 60                             <option value="{{ item.name }}">{{ item.desc }}</option>
 61                         {% endfor %}
 62                     </select>
 63                     <button type="submit" class="btn btn-primary">Go</button>
 64                 </div>
 65 
 66                 <table class="table table-stripped table-hover">
 67 
 68                     <thead>
 69                     <tr>
 70                         {% for foo in sl.get_header %}
 71                             <td>{{ foo }}</td>
 72                         {% endfor %}
 73                     </tr>
 74                     </thead>
 75                     <tbody>
 76                     {% for data in sl.get_body %}
 77                         <tr>
 78                             {% for item in data %}
 79                                 <td>{{ item }}</td>
 80                             {% endfor %}
 81                         </tr>
 82                     {% endfor %}
 83 
 84                     </tbody>
 85 
 86                 </table>
 87             </form>
 88 
 89 
 90             <div class="text-center">
 91                 <nav aria-label="Page navigation">
 92                     <ul class="pagination">
 93                         {{ sl.pagination.page_html|safe }}
 94                     </ul>
 95                 </nav>
 96             </div>
 97 
 98 
 99         </div>
100 
101         <div class="col-md-3">
102             <div class="filter">
103             {% for filter_link_tag in sl.get_filter_link_tags %}
104 
105 
106             <div class="well">{% for data in filter_link_tag %}
107                 <div>{{ data }}</div>
108             {% endfor %}
109             </div>
110 
111             {% endfor %}
112             </div>
113         </div>
114     </div>
115 </div>
116 <script src="/static/js/hello.js">
117     {#    我们的js代码里面需要注意单引号和双引号的区别,不能轻易改写,还有选择器,组合选择器之间需要空格#}
118 </script>
119 </body>
120 </html>
前端HTML模板

 

静态文件:

技术分享图片
1 $("#action-toggle").click(function(){
2     if($(this).prop("checked")){
3         $("tbody :checkbox").prop("checked",true);
4     }else{
5         $("tbody :checkbox").prop("checked",false);
6     }
7 });
js代码

 

以上是关于django-admin 仿写stark组件action,filter筛选过滤,search查询的主要内容,如果未能解决你的问题,请参考以下文章

stark组件4_pop功能

Django-CRM项目学习-stark的action以及多级筛选功能

Django——stark组件

django 之 stark组件

Stark组件

10.20stark组件已经完工