fifth_自制分页_search_保留条件
1.实现思想

2.样品图

3.代码部分

class Pagination(object): def __init__(self, request, current_page, all_data, per_page_num=2, max_page_count=11): """ 封装分页相关数据 :param request: 请求对象 :param current_page:当前页 :param all_data: 总数据 :param per_page_num: 每页显示的数据条数 :param max_page_count: 最多显示的页码个数 """ try: current_page = int(current_page) # 是否有跳转页 except Exception as e: current_page = 1 # 没有则默认1 # print("-------333", type(current_page)) if current_page < 1: current_page = 1 # 如果为负,则默认1,容错机制 self.current_page = current_page self.all_count = len(all_data) # 获取总数据长度即有多少个对象 self.per_page_num = per_page_num all_pager, tmp = divmod(self.all_count, per_page_num) # 商除,取得商(显示多少页)和余(多出来的数据) if tmp: all_pager += 1 # 如果有多出来的数据,则页数+1 # 定义数据便于调用 self.all_pager = all_pager self.max_page_count = max_page_count self.max_page_count_half = int((max_page_count - 1) / 2) # 最大显示页数的一半 self.request = request import copy self.params = copy.deepcopy(self.request.GET) # 深度拷贝,因为之间拿出来的Queryset是不能修改值的 @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): # 配置html页面 if self.all_pager <= self.max_page_count: # 如果总页码小于11个 pageRange = range(1, self.all_pager + 1) else: # 如果大于总页码,分以下3种情况 # 如果当前页面小于页面上最多显示的(11-1)/2页码 if self.current_page <= self.max_page_count_half: pageRange = range(1, self.max_page_count + 1) # else: # 如果当前页大于5,分以下两种情况 if (self.current_page + self.max_page_count_half) > self.all_pager: # 如果当前页数+5大于总页数,即已经翻到最后面了 pageRange = range(self.all_pager - self.max_page_count + 1, self.all_pager + 1) else: # 当前页码即是处于中间位置 pageRange = range(self.current_page - self.max_page_count_half, self.current_page + self.max_page_count_half + 1) page_html_list = [] # 设置前端页面 # 开始为首页和上一页添加a标签 self.params["page"] = 1 # 页数为1 first_page = '<nav aria-label="Page navigation"><ul class="pagination"><li><a href="?%s">首页</a></li>' % ( self.params.urlencode(),) # 首页跳转解析源码 page_html_list.append(first_page) self.params['page'] = self.current_page - 1 if self.current_page <= 1: prev_page = '<li class="disabled"><a href="#">上一页</a></li>' # 如果小于等于1了,表名已经在第一页不能跳转了 else: prev_page = '<li><a href="?%s">上一页</a></li>' % (self.params.urlencode(),) # 否则 page_html_list.append(prev_page) # 为每个数字页码添加a标签 for i in pageRange: self.params['page'] = i if i == self.current_page: # 如果页码刚好等于当前页面,则添加active 特殊样式 temp = '<li class="active"><a href="?%s">%s</a></li>' % (self.params.urlencode(), i,) else: # 否则添加普通样式即可 temp = '<li><a href="?%s">%s</a></li>' % (self.params.urlencode(), i,) page_html_list.append(temp) # 写入 search_page = '<li><a href="?%s" id="search_id"><span aria-label="true">搜索</span</a></li> </ul></nav>' % ( self.params.urlencode(),) # 为下一页和尾页添加a标签 self.params['page'] = self.current_page + 1 if self.current_page >= self.all_pager: # 如果是最后一页,则添加不可点击特殊样式 next_page = '<li class="disabled"><a href="#">下一页</a></li>' else: next_page = '<li><a href="?%s">下一页</a></li>' % (self.params.urlencode(),) page_html_list.append(next_page) self.params['page'] = self.all_pager last_page = '<li><a href="?%s">尾页</a></li>' % (self.params.urlencode()) page_html_list.append(last_page) val = self.request.GET.get('q') if not val: text_page = '<li><span><input type="text" id="text_id" style="height: 20px;width: 40px"></span></li>' page_html_list.append(text_page) page_html_list.append(search_page) script_page = '\n<script>' \ '\n\t$("#text_id").on("blur", function () {' \ '\n\t\tlet num = $("#text_id").val();' \ '\n\t\tlet a_obj = document.getElementById("search_id");' \ '\n\t\tconsole.log(num);' \ '\n\t\ta_obj.href ="?page=" + num;})\n' \ '\n</script>' page_html_list.append(script_page) return ''.join(page_html_list) # 将所有代码拼接起来

from django.urls import path, re_path from django.shortcuts import HttpResponse, render, redirect from app01 import models from django.urls import reverse from django.utils.safestring import mark_safe from django.core.exceptions import FieldDoesNotExist class ShowList(object): def __init__(self, request, config_obj, queryset): self.request = request # 请求对象 self.config_obj = config_obj # 当前调用者函数对象 self.queryset = queryset # 显示数据 self.pager_queryset = self.get_pager_queryset() # 分页机制 def get_pager_queryset(self): from stark.utils.page import Pagination current_page = self.request.GET.get("page", 1) self.pagination = Pagination(self.request, current_page, self.queryset, per_page_num=self.config_obj.per_page_num or 5) queryset = self.queryset[self.pagination.start:self.pagination.end] # 切片取展示的数据,到当前页 print("get_pager_queryset----queryset", queryset, type(queryset)) return queryset def get_header(self): # 构建表头,将表头和表内数据分开成两个列表 header_list = [] for field_or_func in self.config_obj.get_new_list_display(): # print("field_or_func", field_or_func) if callable(field_or_func): # 判断是否可调用对象 val = field_or_func(self, header=True) # 将函数header改成True并调用得到值做表头 header_list.append(val) # 是的话调用函数并将值添加到表头 else: if field_or_func == "__str__": val = self.config_obj.model._meta.model_name.upper() else: # app01.Book.title 取到对应models模型字段的对象 field_obj = self.config_obj.model._meta.get_field(field_or_func) # 获取到对应表头名 val = field_obj.verbose_name header_list.append(val) return header_list def get_body(self): new_data = [] for obj in self.pager_queryset: # 将当前表数据循环获取出每一条的对象 temp = [] for field_or_func in self.config_obj.get_new_list_display(): # 循环新display列表, 并将内容取出来 if callable(field_or_func): # 如果是可调用对象 val = field_or_func(self.config_obj, obj) # 运行函数并获取值 else: try: from django.db.models.fields.related import ManyToManyField field_obj = self.config_obj.model._meta.get_field(field_or_func) # 获取到对象 # 判断是否多对多字段,判断该函数需要导入上面模块,如果直接写多对多则报错,需要自己写函数 if type(field_obj) == ManyToManyField: raise Exception("list_display can't many To many") # 判断字段是否拥有 choices 属性 if field_obj.choices: # obj.get_state_display() 该方法能获取到choices对象目前的值 val = getattr(obj, "get_%s_display" % field_or_func)() else: # 所有对象空间如果有属性值,利用以下方法即可反射获取值 val = getattr(obj, field_or_func) if field_or_func in self.config_obj.list_display_links: # 如果有自定制link方法,则走下面这条语句,obj则是当前表的该条对象,link方法完成 val = mark_safe("<a href='%s'>%s</a>" % (self.config_obj.get_change_url(obj), val)) except FieldDoesNotExist as e: # 进入publish时,走上面语句则会报错,就会进入下面这个方法 val = getattr(obj, field_or_func)() # 直接用str获取当前对象的数据即可 temp.append(val) # 将数据一行一行的添加 new_data.append(temp) print("new_data", new_data) return new_data class ModelStark(object): """ 默认配置类 """ list_display = ("__str__",) # 创建一个变量,如果用户没有自定义则走默认的模型str(str在app01的models定义了),用来满足自定制 list_display_links = [] # 点击书名等也可以进行编辑 models_form_class = None # 设定默认使用默认模板,进行添加编辑业务数据显示及添加修改 per_page_num = None # 定义默认指定显示数据为0 search_fields = [] # 设定默认搜索区域即对象字段 search_val = None # 设置搜索跳转后,搜索框的内容 def __init__(self, model): self.model = model self.model_name = self.model._meta.model_name # 获取model名称 self.app_label = self.model._meta.app_label # 获取model所在app名称 # 反向解析当前访问表的增删改查URL def get_list_url(self): # 反向解析当前表的URL list_url = reverse("%s_%s_list" % (self.app_label, self.model_name)) return list_url def get_add_url(self): # 反向解析当前表的添加URL add_url = reverse("%s_%s_add" % (self.app_label, self.model_name)) return add_url def get_delete_url(self, obj): # 反向解析当前表的删除URL,args 获取当前obj delete_url = reverse("%s_%s_delete" % (self.app_label, self.model_name), args=(obj.pk,)) return delete_url def get_change_url(self, obj): # 反向解析当前表的编辑URL change_url = reverse("%s_%s_change" % (self.app_label, self.model_name), args=(obj.pk,)) return change_url # 三个默认的列,即功能 def show_checkbox(self, obj=None, header=False): """ mark_safe: 将添加的字节进行编译,HTML默认将添加字节做字符串处理 :param obj: None :param header: True :return: """ if header: return mark_safe("<input type='checkbox'>") return mark_safe("<input type='checkbox'>") def show_delbtn(self, obj=None, header=False): if header: return "删除" return mark_safe("<a href='%s'>删除</a>" % self.get_delete_url(obj)) def show_editbtn(self, obj=None, header=False): """ mark_safe: 将添加的字节进行编译,HTML默认将添加字节做字符串处理 :param obj: 当前表的对象 :param header: 如果添加header命名则走自定制页面 :return: """ if header: return "编辑" return mark_safe("<a href='%s'>编辑</a>" % self.get_change_url(obj)) # 构建新的list_display def get_new_list_display(self): temp = [] temp.extend(self.list_display) # 迭代增加元素,即将list_display拆开一个个添加进去 temp.append(ModelStark.show_delbtn) # 将删除模型类对象添加进去 temp.append(ModelStark.show_editbtn) # 将编辑模型类对象添加进去 temp.insert(0, ModelStark.show_checkbox) # 多选按钮插入到第0个位置 """ [<function ModelStark.show_checkbox at 0x0000025CCDED1C80>, 'title', 'price', 'state', 'publisher', <function BookConfig.show_authors at 0x0000025CCDED1400>, <function ModelStark.show_delbtn at 0x0000025CCDED1D08>, <function ModelStark.show_editbtn at 0x0000025CCDED1D90>] """ return temp # 此时temp如上 def get_search_condition(self, request): val = request.GET.get('q') from django.db.models import Q # 导入过滤功能 q = Q() # 由于的Q不能接收字符串进行过滤,所以需要实例化q 来过滤 if val: self.search_val = val q.connector = "or" # 实例化q 只有且的功能,所以加上or for field in self.search_fields: q.children.append((field + "__contains", val)) # 拿出对应的搜索以元祖的形式添加 else: self.search_val = None # 如果前端搜索框没有请求,则清空内容,就不会自带着内容了 # 返回搜索到的表的对象 return q def list_view(self, request): queryset = self.model.objects.all() # 获取当前对象的Queryset,即当前表数据 queryset = queryset.filter(self.get_search_condition(request)) # 构建展示数据 show_list = ShowList(request, self, queryset) table_name = self.model._meta.verbose_name # 当前表的名字 add_url = self.get_add_url() # 将add_url定义,此时local就会自动传到前端 return render(request, 'stark/list_view.html', locals()) def get_model_form(self): from django.forms import ModelForm # 导入ModelForm模块,即将model 转化成form 的组件 # 设定默认模板 class BaseModelForm(ModelForm): # 设定默认模板,其中__all__ class Meta: model = self.model # 告诉ModelForm是关联哪张表 fields = "__all__" # 展示全部数据字段 # 如果没有自定制则返回默认展示方式 return self.models_form_class or BaseModelForm def add_view(self, request): base_model_form = self.get_model_form() # 获取当前展示字段 if request.method == "GET": form_obj = base_model_form() # 获取当前展示字段并传到前端 return render(request, 'stark/add_view.html', locals()) else: form_obj = base_model_form(request.POST) if form_obj.is_valid(): # 判断用户输入的值是否符合model的规定 form_obj.save() # 如果符合则保存到数据库,并返回查看页面 return redirect(self.get_list_url()) else: return render(request, "stark/add_view.html", locals()) # 否则将携带报错信息的form返回当前页面提示用户 def change_view(self, request, id): base_model_form = self.get_model_form() # 获取需要展示字段 edit_obj = self.model.objects.filter(pk=id).first() if request.method == 'GET': form_obj = base_model_form(instance=edit_obj) # 这句话让modelform可以显示valus内容以供编辑 return render(request, 'stark/change_view.html', locals()) else: form_obj = base_model_form(request.POST, instance=edit_obj) # instance 是告诉modelform是编辑,且编辑哪个对象 if form_obj.is_valid(): form_obj.save() return redirect(self.get_list_url()) else: return render(request, 'stark/change_view.html', locals()) def delete_view(self, request, id): if request.method == 'POST': self.model.objects.filter(pk=id).delete() return redirect(self.get_list_url()) # 不删除则返回 list_url = self.get_list_url() return render(request, 'stark/delete_view.html', locals()) @property def get_urls(self): # 二级分发路径, 反向解析各种路径 temp = [ path("", self.list_view, name="%s_%s_list" % (self.app_label, self.model_name)), path("add/", self.add_view, name="%s_%s_add" % (self.app_label, self.model_name)), re_path("(\d+)/change/", self.change_view, name="%s_%s_change" % (self.app_label, self.model_name)), re_path("(\d+)/delete/", self.delete_view, name="%s_%s_delete" % (self.app_label, self.model_name)), ] return (temp, None, None) class StarkSite: """ stark 全局类 """ def __init__(self): # 类的单例模式,提高执行效率(都是用同一空间),减少开辟多个内存空间 self._registry = {} def register(self, model, admin_class=None, **options): # model就是用户模型类(指向空间,即book或publish),admin_class 如果用户没有自己创建样式装修则用默认的 admin_class = admin_class or ModelStark # or 的方法,选择为非0的对象赋值 self._registry[model] = admin_class(model) # 给字典不断添加键值对,以model为键,配置类为值 def get_urls(self): # 动态为注册的模型类创建增删改查URL # 创建temp存放path temp = [] # {Book:ModelAdmin(Book),Publish:ModelAdmin(Publish)} for model, config_obj in self._registry.items(): # 将上面函数拿到的各个模型类键值对提取出来,config_obj就是 model_name = model._meta.model_name # 该方法是拿model的名字(字符串) app_label = model._meta.app_label # 该方法是拿model所在app的字符串 """ 二级分发小例子: urls = [ path('index/', index), path('book/',([ path('test01/', test01), path('test02/', test02), ],None,None)) ] """ # 给temp添加path,即book_manage/book 或 book_manage/publishb # config_obj.get_urls 即是增删改查的路径(二级分发),默认就会有自己的二级temp temp.append( path("%s/%s/" % (app_label, model_name), config_obj.get_urls) ) return temp @property def urls(self): # property 方法是使调用它的时候直接使用urls即可调用(默认Admin做的,可以参考urls里面的Admin) return self.get_urls(), None, None # 调用时返回这些值,None是固定传的,一个是app名字一个是空间名字所以直接None site = StarkSite() """ 将全局类实例化成一个对象,所有的调用都使用这个实例化对象, 这样空间就永远是一个,只是实例化键值对来指向它 """

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.css"> <script src="/static/bootstrap/js/jquery-3.3.1.js"></script> </head> <body> <h3>查看数据</h3> <div class="container"> <div class="row"> <div class="panel-heading"> <h3 class="panel-title"></h3> </div> <div class="panel-body panel-info"> {% if show_list.config_obj.search_fields %} <form action="" method="get"> <div class="input-group pull-right" style="width: 400px"> <input value="{{ show_list.config_obj.search_val|default:'' }}" type="text" name="q" class="form-control" placeholder="Search for..."> <span class="input-group-btn"> <button class="btn btn-default">Search!</button> </span> </div> </form> {% endif %} <div> <a href="{{ add_url }}" class="btn btn-primary ">添加{{ table_name }}</a> </div> </div> <table class="table table-striped" id="-------"> <thead> <tr> {% for item1 in show_list.get_header %} <th>{{ item1 }}</th> {% endfor %} </tr> </thead> <tbody> {% for item in show_list.get_body %} <tr> {% for info in item %} <td>{{ info }}</td> {% endfor %} </tr> {% endfor %} </tbody> </table> <div class="pull-right"> {{ show_list.pagination.page_html|safe }} </div> </div> </div> </body> </html>

from stark.servise.sites import site, ModelStark from .models import * from django.forms import ModelForm class BookModelForm(ModelForm): class Meta: model = Book fields = "__all__" error_messages = { "title": {"required": " 该字段不能为空!"} } class BookConfig(ModelStark): def show_authors(self, obj=None, header=False): if header: return "作者信息" return " ".join([author.name for author in obj.authors.all()]) # 自定制的模板,继承ModelStark,也就是还有没有覆盖的ModelStark变量及方法 list_display = ["title", "price", "state", "publisher", show_authors] list_display_links = ['title', ] model_form_class = BookModelForm per_page_num = 3 search_fields = ['title', 'price'] site.register(Book, BookConfig) # 疑问,为什么执行了两遍 site.register(Publish) # print(site._registry)
所有功能全部完成

浙公网安备 33010602011771号