stark——分页、search、actions
一、分页
1、引入自定义分页组件
在/stark目录下创建utils工具包目录,复制page.py到该目录下,文件中有之前自定义的分页组件。

class Pagination(object): def __init__(self, current_page, all_count, base_url, params, per_page_num=8, pager_count=11, ): """ 封装分页相关数据 :param current_page: 当前页 :param all_count: 数据库中的数据总条数 :param per_page_num: 每页显示的数据条数 :param base_url: 分页中显示的URL前缀 :param pager_count: 最多显示的页码个数 """ try: current_page = int(current_page) except Exception as e: current_page = 1 if current_page < 1: current_page = 1 self.current_page = current_page self.all_count = all_count self.per_page_num = per_page_num self.base_url = base_url # 总页码 all_pager, tmp = divmod(all_count, per_page_num) if tmp: all_pager += 1 self.all_pager = all_pager self.pager_count = pager_count # 最多显示页码数 self.pager_count_half = int((pager_count - 1) / 2) import copy params = copy.deepcopy(params) params._mutable = True self.params = params # self.params : {"page":77,"title":"python","nid":1} @property def start(self): return (self.current_page - 1) * self.per_page_num @property def end(self): return self.current_page * self.per_page_num def page_html(self): # 如果总页码 < 11个: if self.all_pager <= self.pager_count: pager_start = 1 pager_end = self.all_pager + 1 # 总页码 > 11 else: # 当前页如果<=页面上最多显示(11-1)/2个页码 if self.current_page <= self.pager_count_half: pager_start = 1 pager_end = self.pager_count + 1 # 当前页大于5 else: # 页码翻到最后 if (self.current_page + self.pager_count_half) > self.all_pager: pager_start = self.all_pager - self.pager_count + 1 pager_end = self.all_pager + 1 else: pager_start = self.current_page - self.pager_count_half pager_end = self.current_page + self.pager_count_half + 1 page_html_list = [] self.params["page"] = 1 first_page = '<li><a href="%s?%s">首页</a></li>' % (self.base_url, self.params.urlencode(),) page_html_list.append(first_page) if self.current_page <= 1: prev_page = '<li class="disabled"><a href="#">上一页</a></li>' else: self.params["page"] = self.current_page - 1 prev_page = '<li><a href="%s?%s">上一页</a></li>' % (self.base_url, self.params.urlencode(),) page_html_list.append(prev_page) for i in range(pager_start, pager_end): # self.params : {"page":77,"title":"python","nid":1} self.params["page"] = i # {"page":72,"title":"python","nid":1} if i == self.current_page: temp = '<li class="active"><a href="%s?%s">%s</a></li>' % (self.base_url, self.params.urlencode(), i,) else: temp = '<li><a href="%s?%s">%s</a></li>' % (self.base_url, self.params.urlencode(), i,) page_html_list.append(temp) if self.current_page >= self.all_pager: next_page = '<li class="disabled"><a href="#">下一页</a></li>' else: self.params["page"] = self.current_page + 1 next_page = '<li><a href="%s?%s">下一页</a></li>' % (self.base_url, self.params.urlencode(),) page_html_list.append(next_page) self.params["page"] = self.all_pager last_page = '<li><a href="%s?%s">尾页</a></li>' % (self.base_url, self.params.urlencode(),) page_html_list.append(last_page) return ''.join(page_html_list)
2、整合展示数据showlist类实现代码解耦
将list_view函数中的构建表头代码挪移到ShowList类的get_header函数下,将将list_view函数中的构建表单代码挪移到ShowList类的get_body函数下。
/stark/serivce/stark.py:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | class ShowList( object ): """展示页面类""" def __init__( self , config, data_list): self .config = config # 接收传递过来的配置类对象 self .data_list = data_list # 接收传递过来的当前表的所有对象 def get_header( self ): """构建表头""" header_list = [] print ( "header" , self .config.new_list_display()) # [checkbox ,"__str__", edit ,deletes] for field in self .config.new_list_display(): if callable (field): # 如果是函数 val = field( self , header = True ) header_list.append(val) else : # 如果是字符串 if field = = "__str__" : header_list.append( self .config.model._meta.model_name.upper()) # 当前模型表名 else : # 如果不是"__str__" # header_list.append(field) val = self .config.model._meta.get_field(field).verbose_name header_list.append(val)<br> return header_list def get_body( self ): """构建表单数据""" new_data_list = [] for obj in self .data_list: temp = [] for field in self .config.new_list_display(): # ["__str__", ] ["pk","name","age",edit] if callable (field): val = field( self .config, obj) else : val = getattr (obj, field) if field in self .config.list_display_links: # _url = reverse("%s_%s_change" % (app_label, model_name), args=(obj.pk,)) _url = self .config.get_change_url(obj) val = mark_safe( "<a href='%s'>%s</a>" % (_url, val)) temp.append(val) new_data_list.append(temp)<br> return new_data_list class ModelStark( object ): """代码省略""" def list_view( self , request): data_list = self .model.objects. all () # 拿到对应表所有的对象 show_list = ShowList( self , data_list) # 传入self,即将当前ModelStark的实例对象传给ShowList<br> header_list = show_list.get_header()<br> new_data_list = show_list.get_body() # 构建一个查看url add_url = self .get_add_url() return render(request, "list_view.html" , locals ()) |
(1)在list_view函数下执行ShowList实例化,在实例化时传入:
self:即当前ModelStark的实例对象;data_list:对应表所有的对象。
(2)在新的类ShowList通过__init__方法来接收者两个参数:
1 2 3 4 5 | class ShowList( object ): """展示页面类""" def __init__( self , config, data_list): self .config = config # 接收传递过来的配置类对象 self .data_list = data_list # 接收传递过来的当前表的所有对象 |
(3)给模板传递正确的数据
由于模板接收的数据是header_list和new_data_list。需要给get_header和get_body函数添加返回值。
同时在list_view中构建header_list和new_data_list变量:
1 2 3 | show_list = ShowList( self , data_list) # 传入self,即将当前ModelStark的实例对象传给ShowList header_list = show_list.get_header() new_data_list = show_list.get_body() |
3、给查看页码添加分页
(1)service/stark.py:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | from stark.utils.page import Pagination class ShowList( object ): """展示页面类""" def __init__( self , config, data_list, request): self .config = config # 接收传递过来的配置类对象 self .data_list = data_list # 接收传递过来的当前表的所有对象 self .request = request # 分页 data_count = self .data_list.count() current_page = int ( self .request.GET.get( "page" , 1 )) # 默认是第一页 base_path = self .request.path self .pagination = Pagination(current_page, data_count, base_path, self .request.GET, per_page_num = 1 , pager_count = 11 ,) self .page_data = self .data_list[ self .pagination.start: self .pagination.end] """代码省略""" class ModelStark( object ): """代码省略""" def list_view( self , request): data_list = self .model.objects. all () # 拿到对应表所有的对象 show_list = ShowList( self , data_list, request) # 传入self,即将当前ModelStark的实例对象传给ShowList header_list = show_list.get_header() new_data_list = show_list.get_body() # 构建一个查看url add_url = self .get_add_url() print ( "add_url" , add_url) return render(request, "list_view.html" , locals ()) |
(1)list_view函数中创建ShowList示例时多传入了一个request参数。ShowList拿到request后获取GET请求数据和请求路径:
1 2 3 4 5 6 7 8 9 10 | class ShowList( object ): """展示页面类""" def __init__( self , config, data_list, request): self .config = config # 接收传递过来的配置类对象 self .data_list = data_list # 接收传递过来的当前表的所有对象 self .request = request # <WSGIRequest: GET '/stark/app01/book/?page=2'> # 分页 data_count = self .data_list.count() current_page = int ( self .request.GET.get( "page" , 1 )) # 默认是第一页 base_path = self .request.path # /stark/app01/book/ |
(2)实例化pagination,获取页码数据
引入自定义分页组件,将参数传入完成pagination实例化。获取当前页码数据:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | class ShowList( object ): """展示页面类""" def __init__( self , config, data_list, request): '''代码省略''' self .pagination = Pagination(current_page, data_count, base_path, self .request.GET, per_page_num = 1 , pager_count = 11 ,) print ( "data_list" , self .data_list) # data_list <QuerySet [<Book: python葵花宝典>, <Book: go>, <Book: java>]> self .page_data = self .data_list[ self .pagination.start: self .pagination.end] print ( "page_data" , self .page_data) # page_data <QuerySet [<Book: python葵花宝典>]> '''代码省略''' def get_body( self ): """构建表单数据""" new_data_list = [] # for obj in self.data_list: for obj in self .page_data: # 当前页面的数据 '''代码省略''' |
(3)list_view.html添加分页
1 2 3 4 5 6 7 8 9 10 11 12 | < body > < h4 >数据列表</ h4 > < div class="container"> < div class="row"> < div class="col-md-9"....代码省略..> < nav > < ul class="pagination"> {{ show_list.pagination.page_html|safe }} </ ul > </ nav > </ div > </ div > |
(4)显示效果如下:
二、search查询
1、在查看页面添加搜索框
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | < h4 >数据列表</ h4 > < div class="container"> < div class="row"> < div class="col-md-9"> < a href="{{ add_url }}" class="btn btn-primary">添加数据</ a > < form action="" class="pull-right"> < input type="text" name="q"> < button >搜索</ button > </ form > < table class="table table-bordered table-striped".....> </ div > < nav.... > </ div > </ div > |
输入查询内容后,点击搜索按钮,发送的get请求如下所示:
2、查询匹配

# -*- coding:utf-8 -*- __author__ = 'Qiushi Huang' from django.conf.urls import url from django.shortcuts import HttpResponse, render, redirect from django.utils.safestring import mark_safe from django.urls import reverse from stark.utils.page import Pagination class ShowList(object): """展示页面类""" def __init__(self, config, data_list, request): self.config = config # 接收传递过来的配置类对象 ModelStark的实例对象 self.data_list = data_list # 接收传递过来的当前表的所有对象 self.request = request # <WSGIRequest: GET '/stark/app01/book/?page=2'> # 分页 data_count = self.data_list.count() current_page = int(self.request.GET.get("page", 1)) # 默认是第一页 base_path = self.request.path # /stark/app01/book/ self.pagination = Pagination(current_page, data_count, base_path, self.request.GET, per_page_num=1, pager_count=11,) print("data_list", self.data_list) # data_list <QuerySet [<Book: python葵花宝典>, <Book: go>, <Book: java>]> self.page_data = self.data_list[self.pagination.start:self.pagination.end] print("page_data", self.page_data) # page_data <QuerySet [<Book: python葵花宝典>]> def get_header(self): """构建表头""" header_list = [] print("header", self.config.new_list_display()) # [checkbox ,"__str__", edit ,deletes] for field in self.config.new_list_display(): if callable(field): # 如果是函数 val = field(self, header=True) header_list.append(val) else: # 如果是字符串 if field == "__str__": header_list.append(self.config.model._meta.model_name.upper()) # 当前模型表名 else: # 如果不是"__str__" # header_list.append(field) val = self.config.model._meta.get_field(field).verbose_name header_list.append(val) return header_list def get_body(self): """构建表单数据""" new_data_list = [] # for obj in self.data_list: for obj in self.page_data: # 当前页面的数据 temp = [] for field in self.config.new_list_display(): # ["__str__", ] ["pk","name","age",edit] if callable(field): val = field(self.config, obj) else: val = getattr(obj, field) if field in self.config.list_display_links: # _url = reverse("%s_%s_change" % (app_label, model_name), args=(obj.pk,)) _url = self.config.get_change_url(obj) val = mark_safe("<a href='%s'>%s</a>" % (_url, val)) temp.append(val) new_data_list.append(temp) return new_data_list class ModelStark(object): """默认类,定制配置类""" list_display = ["__str__",] list_display_links = [] modelform_class = [] search_fields = [] def __init__(self, model, site): self.model = model self.site = site # 删除、编辑,复选框 def edit(self, obj=None, header=False): """编辑""" if header: # 如果是表头显示操作 return "操作" _url = self.get_change_url(obj) return mark_safe("<a href='%s'>编辑</a>" % _url) def deletes(self, obj=None, header=False): """删除""" if header: # 如果是表头显示操作 return "操作" _url = self.get_delete_url(obj) # return mark_safe("<a href='%s/change'>删除</a>" % obj.pk) return mark_safe("<a href='%s/'>删除</a>" % _url) def checkbox(self, obj=None, header=False): """复选框""" if header: # 如果是表头显示操作 return mark_safe("<input id='choice' type='checkbox'>") return mark_safe("<input class='choice_item' type='checkbox'>") def get_modelform_class(self): """用来获取modelform类""" if not self.modelform_class: # 如果没有值 from django.forms import ModelForm from django.forms import widgets as wid class ModelFormDemo(ModelForm): class Meta: model = self.model fields = "__all__" return ModelFormDemo else: # 如果有值说明在用户已经自己定制过了,直接取值 return self.modelform_class def add_view(self, request): ModelFormDemo = self.get_modelform_class() if request.method == "POST": form = ModelFormDemo(request.POST) if form.is_valid(): # 校验字段全部合格 form.save() return redirect(self.get_list_url()) # 跳转到当前访问表的查看页面 # (精髓)校验有错误返回页面,且包含了错误信息 return render(request, "add_view.html", locals()) form = ModelFormDemo() # 实例化 return render(request, "add_view.html", locals()) def delete_view(self, request, id): url = self.get_list_url() if request.method == "POST": self.model.objects.filter(pk=id).delete() return redirect(url) # self.model.objects.filter(pk=id).delete() return render(request, "delete_view.html", locals()) def change_view(self, request, id): """编辑视图""" ModelFormDemo = self.get_modelform_class() # 编辑对象 edit_obj = self.model.objects.filter(pk=id).first() if request.method == "POST": form = ModelFormDemo(request.POST, instance=edit_obj) # instance就是给这个记录更改为最新的数据 if form.is_valid(): # 校验字段全部合格 form.save() return redirect(self.get_list_url()) # 跳转到当前访问表的查看页面 # (精髓)校验有错误返回页面,且包含了错误信息 return render(request, "add_view.html", locals()) form = ModelFormDemo(instance=edit_obj) # 用instance放入编辑对象就有了编辑数据 return render(request, "change_view.html", locals()) def new_list_display(self): """返回新的列表""" temp = [] temp.append(ModelStark.checkbox) # 在列表中放一个checkbox名字 temp.extend(self.list_display) # 扩展进一个列表["pk","name","age"] if not self.list_display_links: # 如果没有值 temp.append(ModelStark.edit) # temp.append(ModelStark.edit) # edit函数名 temp.append(ModelStark.deletes) # deletes函数名 return temp # 返回新的列表 def get_change_url(self,obj): model_name = self.model._meta.model_name app_label = self.model._meta.app_label _url = reverse("%s_%s_change" % (app_label, model_name), args=(obj.pk,)) return _url def get_delete_url(self, obj): model_name = self.model._meta.model_name app_label = self.model._meta.app_label _url = reverse("%s_%s_delete" % (app_label, model_name), args=(obj.pk,)) return _url def get_add_url(self): model_name = self.model._meta.model_name app_label = self.model._meta.app_label _url = reverse("%s_%s_add" % (app_label, model_name)) return _url def get_list_url(self): model_name = self.model._meta.model_name app_label = self.model._meta.app_label _url = reverse("%s_%s_list" % (app_label, model_name)) return _url def get_search_condition(self, request): key_word = request.GET.get("q", "") # 取不到q则直接取空 self.key_word = key_word from django.db.models import Q search_connection = Q() if key_word: # 判断key_word是否为空 search_connection.connector = "or" # 查询条件设置为或 for search_field in self.search_fields: # self.search_fields # ['title', "price"] search_connection.children.append((search_field + "__contains", key_word)) return search_connection def list_view(self, request): # 获取search的Q对象 search_condition = self.get_search_condition(request) # 筛选当前表获取的数据 data_list = self.model.objects.all().filter(search_condition) # 获取showlist展示页面 show_list = ShowList(self, data_list, request) header_list = show_list.get_header() new_data_list = show_list.get_body() # 构建一个查看url add_url = self.get_add_url() print("add_url", add_url) return render(request, "list_view.html", locals()) def get_urls_2(self): temp = [] # 用name取别名app名+model名+操作名可以保证别名不会重复 model_name = self.model._meta.model_name app_label = self.model._meta.app_label temp.append(url(r"^add/", self.add_view, name="%s_%s_add" % (app_label, model_name))) temp.append(url(r"^(\d+)/delete/", self.delete_view, name="%s_%s_delete" % (app_label, model_name))) temp.append(url(r"^(\d+)/change/", self.change_view, name="%s_%s_change" % (app_label, model_name))) temp.append(url(r"^$", self.list_view, name="%s_%s_list" % (app_label, model_name))) return temp @property def urls_2(self): return self.get_urls_2(), None, None # [], None, None class StarkSite(object): """site单例类""" def __init__(self): self._registry = {} def register(self, model, stark_class=None, **options): """注册""" if not stark_class: # 如果注册的时候没有自定义配置类,执行 stark_class = ModelStark # 配置类 # 将配置类对象加到_registry字典中,键为模型类 self._registry[model] = stark_class(model, self) # _registry={'model':admin_class(model)} def get_urls(self): """构造一层url""" temp = [] for model, stark_class_obj in self._registry.items(): # model:一个模型表 # stark_class_obj:当前模型表相应的配置类对象 model_name = model._meta.model_name app_label = model._meta.app_label # 分发增删改查 temp.append(url(r"^%s/%s/" % (app_label, model_name), stark_class_obj.urls_2)) """ path('app01/userinfo/',UserConfig(Userinfo,site).urls2), path('app01/book/',ModelStark(Book,site).urls2), """ return temp @property def urls(self): return self.get_urls(), None, None site = StarkSite() # 单例对象
(1)Q查询
filter() 等方法中的关键字参数查询都是一起进行“AND” 的。 如果需要执行更复杂的查询(例如OR 语句)可以使用Q对象。可以组合& 和| 操作符以及使用括号进行分组来编写任意复杂的Q 对象。同时,Q 对象可以使用~ 操作符取反,这允许组合正常的查询和取反(NOT) 查询。
在这里由于需要循环self.search_fields,且拿到的都是一个个字符串。所以需要使用Q对象实例化的用法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | from .models import * def test(request): from django.db.models import Q # Q查询普通写法: ret = Book.objects. all (). filter (Q(title = "go" )|Q(price = 103 )) print ( "ret" , ret) # ret <QuerySet [<Book: go>]> # Q查询特殊用法: q = Q() q.connectiion = "or" q.children.append(( "title" , "go" )) q.children.append(( "price" , 103 )) print ( "q" , q) # q (AND: ('title', 'yuan'), ('price', 123)) return HttpResponse(ret, q) |
Q查询特殊用法应用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | class ModelStark( object ): def list_view( self , request): key_word = request.GET.get( "q" ) from django.db.models import Q search_connection = Q() search_connection.connector = "or" # 查询条件设置为或 for search_field in self .search_fields: # self.search_fields # ['title', "price"] search_connection.children.append((search_field, key_word)) data_list = self .model.objects. all (). filter (search_connection) # 获取showlist展示页面 show_list = ShowList( self , data_list, request) header_list = show_list.get_header() new_data_list = show_list.get_body() # 构建一个查看url add_url = self .get_add_url() print ( "add_url" , add_url) return render(request, "list_view.html" , locals ()) |
(2)基于双下划线的模糊查询
1 2 3 4 5 6 7 | # __startwith: 以...开头 ret = Book.objects. filter (title__startswith = "py" ) print (ret) # <QuerySet [<Book: python红宝书>]> # __contains:带有...字符 ret = Book.objects. filter (title__contains = "h" ) print (ret) # <QuerySet [<Book: python红宝书>, <Book: php宝典>]> |
模糊查询应用:search_field+"__contains"
1 2 3 4 5 6 7 8 9 10 11 12 | class ModelStark( object ): def list_view( self , request): key_word = request.GET.get( "q" ) from django.db.models import Q search_connection = Q() if key_word: # 判断key_word是否为空 search_connection.connector = "or" # 查询条件设置为或 for search_field in self .search_fields: # self.search_fields # ['title', "price"] search_connection.children.append((search_field + "__contains" , key_word)) # 获取当前表所有数据 data_list = self .model.objects. all (). filter (search_connection) |
查询效果:
(3)在实例方法中封装模糊查询
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | class ModelStark( object ): def get_search_condition( self , request): key_word = request.GET.get( "q" ) from django.db.models import Q search_connection = Q() if key_word: # 判断key_word是否为空 search_connection.connector = "or" # 查询条件设置为或 for search_field in self .search_fields: # self.search_fields # ['title', "price"] search_connection.children.append((search_field + "__contains" , key_word)) return search_connection def list_view( self , request): # 获取search的Q对象 search_condition = self .get_search_condition(request) # 筛选当前表获取的数据 data_list = self .model.objects. all (). filter (search_condition) # 获取showlist展示页面 show_list = ShowList( self , data_list, request) header_list = show_list.get_header() new_data_list = show_list.get_body() # 构建一个查看url add_url = self .get_add_url() print ( "add_url" , add_url) return render(request, "list_view.html" , locals ()) |
(4)查询提交后,查询框依旧显示搜索的字段
提交查询后相当于获取了一个新的页面,要显示搜索的字段,必须获取该字段并传到新页面中。
1 2 3 4 | < form action="" class="pull-right"> < input type="text" name="q" value="{{ show_list.config.key_word }}"> < button >搜索</ button > </ form > |
这里需要注意show_list这个变量,在ModelStark类中list_view实例方法中,实例化ShowList类时,传递了self,这个self是ModelStark的实例对象,而这个实例对象由ShowList用self.config接收。因此show_list是ShowList的实例对象,具备config属性,show_list.config就是ModelStark的实例对象,具备key_word实例属性。
显示效果:
如果没有输入任何内容直接搜索,搜索框会显示一个None,如果要只显示为空,需要在request.GET.get("q")做如下调整:
1 2 3 4 5 6 7 8 9 10 11 | class ModelStark( object ): def get_search_condition( self , request): key_word = request.GET.get( "q" , "") # 取不到q则直接取空 self .key_word = key_word from django.db.models import Q search_connection = Q() if key_word: # 判断key_word是否为空 search_connection.connector = "or" # 查询条件设置为或 for search_field in self .search_fields: # self.search_fields # ['title', "price"] search_connection.children.append((search_field + "__contains" , key_word)) return search_connection |
3、自定义配置类中配置search_fields则显示搜索框,否则不显示
在list_view.html中先通过if判断show_list.config.search_fields是否有值,有值则显示搜索框,否则不显示。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | <h4>数据列表< / h4> <div class = "container" > <div class = "row" > <div class = "col-md-9" > { # <a href="add/" class="btn btn-primary">添加数据</a> #} <a href = "{{ add_url }}" class = "btn btn-primary" >添加数据< / a> { % if show_list.config.search_fields % } <form action = " " class=" pull - right"> < input type = "text" name = "q" value = "{{ show_list.config.key_word }}" > <button>搜索< / button> < / form> { % endif % } <table class = "table table-bordered table-striped" ......> < / div> <nav> <ul class = "pagination" > {{ show_list.pagination.page_html|safe }} < / ul> < / nav> < / div> < / div> |
在自定义配置类注释掉search_fields字段。
app01/stark.py:
1 2 3 4 5 6 | class BookConfig(ModelStark): list_display = [ "title" , "price" , "publishDate" ] modelform_class = BookModelForm # search_fields = ['title', "price"] site.register(Book, BookConfig) |
页面显示:
三、actions——批量处理
1、admin组件实现actions批量处理
app01/admin.py:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | from django.contrib import admin from .models import Book class BookConfig(admin.ModelAdmin): list_display = [ "title" , "price" ] def patch_init( self , request, queryset): print ( "queryset" , queryset) # queryset <QuerySet [<Book: java>, <Book: python葵花宝典>]> queryset.update(price = 100 ) patch_init.short_description = "批量初始化" actions = [patch_init] admin.site.register(Book, BookConfig) |
注意在这里可以通过patch_init.short_description设置批量操作中文名称。
批量处理前:
批量处理后:
2、在list_view.html中添加action选择框,重构form表单
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | < div class="container"> < div class="row"> < div class="col-md-9"> {# < a href="add/" class="btn btn-primary">添加数据</ a > #} < a href="{{ add_url }}" class="btn btn-primary">添加数据</ a > {% if show_list.config.search_fields %} < form action="" class="pull-right"> < input type="text" name="q" value="{{ show_list.config.key_word }}"> < button >搜索</ button > </ form > {% endif %} < form action=""> < select name="action" id="" style="width: 200px; padding: 5px 8px; display: inline-block"> < option value="">xxxxx</ option > </ select > < button type="submit" class="btn-info">Go</ button > < table class="table table-bordered table-striped"......> < nav..... > </ form > </ div > </ div > </ div > |
注意页面中有两个表单,一个是搜索框的表单,另一个则是包含了aciton、表格、分页。
页面显示效果如下所示:
3、自定义配置actions
(1)构建自定义配置类actions的函数
app01/stark.py:
1 2 3 4 5 6 7 8 9 10 11 12 | class BookConfig(ModelStark): list_display = [ "title" , "price" , "publishDate" ] modelform_class = BookModelForm search_fields = [ 'title' , "price" ] def patch_init( self , request, queryset): print (queryset) patch_init.short_description = "批量初始化" actions = [patch_init] site.register(Book, BookConfig) |
(2)处理actions批量操作函数
/stark/service/stark.py:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | class ShowList( object ): """展示页面类""" def __init__( self , config, data_list, request): self .config = config # 接收传递过来的配置类对象 ModelStark的实例对象 self .data_list = data_list # 接收传递过来的当前表的所有对象 self .request = request # <WSGIRequest: GET '/stark/app01/book/?page=2'> # 分页 data_count = self .data_list.count() current_page = int ( self .request.GET.get( "page" , 1 )) # 默认是第一页 base_path = self .request.path # /stark/app01/book/ self .pagination = Pagination(current_page, data_count, base_path, self .request.GET, per_page_num = 3 , pager_count = 11 ,) self .page_data = self .data_list[ self .pagination.start: self .pagination.end] # actions self .actions = self .config.actions # 拿到配置好的函数对象列表 [patch_init,] def get_action_list( self ): temp = [] for action in self .actions: temp.append({ "name" : action.__name__, # 函数.__name__:拿到函数名 "desc" : action.short_description }) # [{"name": "patch_init", "desc": "批量处理"}] return temp """代码省略""" class ModelStark( object ): """默认类,定制配置类""" list_display = [ "__str__" ,] list_display_links = [] modelform_class = [] search_fields = [] actions = [] # 调用self.actions拿到的是函数 """代码省略""" |
在ModelStark类中默认actions= [],因此在中实例化ShowList时,通过self将actions传递到ShowList类对象,以self.config.actions拿到配置好的函数对象列表交给get_action_list函数处理。
在get_action_list中循环处理函数对象列表,通过函数对象.__name__方式拿到函数名;通过函数对象.short_description拿到描述别名。以字典的形式保存在列表中返回给show_list对象交给页面进行渲染。
(3)进一步构建批量操作表单
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | < form action="" method="post"> {% csrf_token %} < select name="action" id="" style="width: 200px; padding: 5px 8px; display: inline-block"> < option value="">-----------</ option > {% for item in show_list.get_action_list %} < option value="{{ item.name }}">{{ item.desc }}</ option > {% endfor %} </ select > < button type="submit" class="btn-info">Go</ button > < table class="table table-bordered table-striped"......> < nav > < ul class="pagination"> {{ show_list.pagination.page_html|safe }} </ ul > </ nav > </ form > |
show_list.get_action_list拿到函数的返回值temp,循环拿到的item为一个个字典,键为name的是函数名,键为desc的是描述别名。
(4)显示效果
3、实现点选提交
(1)重构checkbox标签
点选批量操作需要拿到当前操作对象的pk值,也需要判断哪些标签被点选提交。
1 2 3 4 5 6 7 8 9 | class ModelStark( object ): """代码省略""" def checkbox( self , obj = None , header = False ): """复选框""" if header: # 如果是表头显示操作 return mark_safe( "<input id='choice' type='checkbox'>" ) return mark_safe( "<input class='choice_item' type='checkbox' name='selected_pk' value='%s'>" % obj.pk) |
(2)list_view处理post请求
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | class ModelStark( object ): def list_view( self , request): if request.method = = "POST" : # action print ( "POST:" , request.POST) action = request.POST.get( "action" ) selected_pk = request.POST.getlist( "selected_pk" ) # 拿到列表 # 反射 # self这里是配置类BookConfig,要在类中找到对应的函数 action_func = getattr ( self , action) # patch_init # 拿到选中状态的pk值对象 queryset = self .model.objects. filter (pk__in = selected_pk) # <QuerySet [<Book: go>]> action_func(request, queryset) # 获取search的Q对象 search_condition = self .get_search_condition(request) # 筛选当前表获取的数据 data_list = self .model.objects. all (). filter (search_condition) # 获取showlist展示页面 show_list = ShowList( self , data_list, request) header_list = show_list.get_header() new_data_list = show_list.get_body() # 构建一个查看url add_url = self .get_add_url() print ( "add_url" , add_url) return render(request, "list_view.html" , locals ()) |
在这里POST请求处理不需要返回值,批量初始化后,数据库已经更改,代码顺着下来紧接着就是查询拿到新的页面。
(3)配置校验

class BookConfig(ModelStark): list_display = ["title", "price", "publishDate"] modelform_class = BookModelForm search_fields = ['title', "price"] def patch_init(self, request, queryset): print(queryset) queryset.update(price=123) patch_init.short_description = "批量初始化" actions = [patch_init] site.register(Book, BookConfig)
显示效果:
4、添加admin批量操作自带的delete功能
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | class ShowList( object ): def __init__( self , config, data_list, request): self .config = config # 接收传递过来的配置类对象 ModelStark的实例对象 self .data_list = data_list # 接收传递过来的当前表的所有对象 self .request = request # <WSGIRequest: GET '/stark/app01/book/?page=2'> # 分页 data_count = self .data_list.count() current_page = int ( self .request.GET.get( "page" , 1 )) # 默认是第一页 base_path = self .request.path # /stark/app01/book/ self .pagination = Pagination(current_page, data_count, base_path, self .request.GET, per_page_num = 3 , pager_count = 11 ,) self .page_data = self .data_list[ self .pagination.start: self .pagination.end] # actions # self.actions = self.config.actions # 拿到配置好的函数对象列表 [patch_init,] self .actions = self .config.new_actions() # 拿到方法运行的返回结果 def get_action_list( self ): """获取自定义批量操作""" temp = [] for action in self .actions: temp.append({ "name" : action.__name__, # 函数.__name__:拿到函数名 "desc" : action.short_description }) # [{"name": "patch_init", "desc": "批量处理"}] return temp class ModelStark( object ): """默认类,定制配置类""" list_display = [ "__str__" ,] list_display_links = [] modelform_class = [] search_fields = [] actions = [] # 调用self.actions拿到的是函数 def __init__( self , model, site): self .model = model self .site = site def patch_delete( self , request, queryset): """默认批量删除操作""" queryset.delete() patch_delete.short_description = "批量删除" def new_actions( self ): """返回所有批量操作""" temp = [] # 默认添加批量删除 temp.append(ModelStark.patch_delete) # 添加自定义action temp.extend( self .actions) return temp |
在new_actions实例方法中,首先可以通过ModelStark.patch_delete固定拿到默认需要添加的批量删除方法。其次ModelStark类中默认actions=[],因此在自定义配置类有配置actions时,self.actions拿到默认配置类配置的列表,如果没有配置拿到空列表。然后通过extend函数扩展列表返回所有批量操作函数。
ShowList实例化时,self.actions属性通过self.config.new_actions()拿到new_actions返回结果。然后在模板上就可以通过show_list.config.actions渲染所有的actions操作了。
显示效果:
1)自定义配置类没有配置actions:
2)自定义配置类配置了actions:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术