前情提要: 本节内容
自定制分页器
保存及查询记录
代码整理,
stark组件search 查询
一:自定制分页器 page
1:创建类 Pagination # 自定制分页器
_ _init_ _ 属性说明
2: 分页器的数据说明,以及简单的数据去除
3 : 页面分析
如果总数为100个, 设置每页8个数据
结果展示如下
4: 展示每页的数据
设置展示本页开始数据索引和本页结束数据索引
由 3 可知 ,每页的展示情况
5 :展示页码
正常展示
显示最后一页
6 : 展示页码 情况
主要分为4种情况
7: 通过后台将前端页码展示进行渲染
知识点补充:
前面在源码中,request.GET 不允许进行修改值
所以用copy 进行深拷贝.
在源码中的_ _ copy _ _ 允许进行修改
这步操作的内容的意思的 当页码更改时,其他的参数不会被改动
这个里面有两个参数,
7 >1:先获取前端数据
7>2: 由于GET方法获取的内容不允许修改,所以进行深拷贝,为self.params, 此为字典形式
7>3 :通过key:valuse 进行新增值 ,这样原来传过来的其他值就不会有问题
7>4:
self.params.urlencode()
这句可以把字典传话成字符串的格式, 如
{'a':1 ,'b':2,'c':3} 转化成
a=1&b=2&c=3
结果:
最后返回加好标签内容的字符串
整段page 代码
# -*- coding: utf-8 -*- # @Time : 2019/4/17 15:17 # @Author : Endless-cloud # @Site : # @File : page.py # @Software: PyCharm ''' ┏┓ ┏┓+ + ┏┛┻━━━┛┻┓ + + ┃ ┃ ┃ ━ ┃ ++ + + + ████━████ ┃+ ┃ ┃ + ┃ ┻ ┃ ┃ ┃ + + ┗━┓ ┏━┛ ┃ ┃ ┃ ┃ + + + + ┃ ┃ Code is far away from bug with the animal protecting ┃ ┃ + 神兽保佑,代码无bug ┃ ┃ ┃ ┃ + ┃ ┗━━━┓ + + ┃ ┣┓ ┃ ┏┛ ┗┓┓┏━┳┓┏┛ + + + + ┃┫┫ ┃┫┫ ┗┻┛ ┗┻┛+ + + + ''' 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 if current_page < 1: # 确保穿过来的参数不能为负数 current_page = 1 self.current_page = current_page # 设定属性 当前页 self.all_count = len(all_data) # 设定转化过数量总数通过len self.per_page_num = per_page_num # 每页最多多少个 all_pager, tmp = divmod(self.all_count, self.per_page_num) # all_pager 总页数 # tmp 余数多少 if tmp: # 如果有余数 all_pager += 1 # 那么页数加1 self.all_pager = all_pager # 总页数 self.max_page_count = max_page_count # 最多显示多少页 self.max_pager_count_half = int((self.max_page_count - 1) / 2) # 最大显示页对半分开数量 self.request = request # 获取当前操作的请求 # request.GET ,获取的是querydict 源码中不予许修改 import copy self.params = copy.deepcopy(self.request.GET) # 从新深复制一遍GET内容,因为request不允许修改 ## 获取前端得到内容,使得跳转的时候内容不会变更,即只更改page内容,其他内容不变更 ''' 分析 all_count =100 总计100个 per_page_num =10 每页使用 current_page 当前操作页 start 开始 end 结束 1 0 10 2 10 20 3 20 30 all_count =100 总计100个 per_page_num =8 每页使用 current_page 当前操作页 start 开始 end 结束 1 0 8 2 8 16 3 16 24 表达式 start =(current_page-1) *per_page_num end =(current_page * per_page_num) ''' @property def start(self): # 展示数据页开始 return (self.current_page - 1) * self.per_page_num # 当前操作页-1 *每页个数 @property def end(self): # 展示数据也结尾 return self.current_page * self.per_page_num # 当前操作页*每页个数 ''' 4种情况: 1:当操作页面不足显示最多页码个数 2:当操作页面内容显示在前11个内(不足减5) 3:当操作页面内容显示在后11个内(不足加5) 4:正常显示 ''' def page_html(self): # 页码效果 # 如果总页码 不足 11个: if self.all_pager <= self.max_page_count: # 如果最大页数 小于设定的展示最多页数 pageRange = range(1, self.all_pager + 1) # 页码范围 1到最大页数 (1,当前最大页数+1) # 总页数 >11 else: # 如果当前操作的页数小于11页的一半 5页 if self.current_page <= self.max_pager_count_half: pageRange = range(1, self.max_page_count + 1) # 页码范围为1到12 (1,12) # 如果当前操作大于5个 else: # 当页码翻到最后 # 如果当前操作页加上5 大于最大页数 if (self.current_page + self.max_pager_count_half) > self.all_pager: pageRange = range(self.all_pager - self.max_page_count + 1, self.all_pager + 1) # 显示 (最大页数-允许显示虽多页数+1,最大页数+1) else: # 正常显示 pageRange = range(self.current_page - self.max_pager_count_half, self.current_page + self.max_pager_count_half + 1) # 显示当前操作页-5 或者当前操作页+5 # 构建分页页码的html # 直接传到前端用html的形式 ,达成自定制效果 page_html_list = [] # 前端字符串 列表 ''' 首页 ''' self.params["page"] = 1 # 修改request返回的内容 first_page = '<nav aria-label="Page navigation"><ul class="pagination"><li><a href="?%s">首页</a></li>' % ( self.params.urlencode(), ) # self.params.urlencode() 将字典转化成字符串 即将page=1 传入%s page_html_list.append(first_page) # 将字符串加入到列表中 self.params["page"] = self.current_page - 1 # 当前操作页 减1 if self.current_page <= 1: prev_page = '<li class="disabled"><a href="#">上一页</a></li>' else: prev_page = '<li><a href="?%s">上一页</a></li>' % (self.params.urlencode(),) # 否则 把page 当前操作页减1 替换 page_html_list.append(prev_page) for i in pageRange: self.params["page"] = i # 是当前page 展示的i if i == self.current_page: # 如果i 等于当前操作页 temp = '<li claskk="active"><a href="?%s">%s</a></li>' % (self.params.urlencode(), i,) # 如果当前操作页==i 跳转i页 显示数据i #当前操作页标色 else: temp = '<li><a href="?%s">%s</a></li>' % (self.params.urlencode(), i,) # 传入的是元祖 # 跳转i 页 ,显示数据i page_html_list.append(temp) self.params["page"] = self.current_page + 1 # 获取的params添加内容 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 # 使page 等于最大页数 last_page = '<li><a href="?%s">尾页</a></li> </ul></nav>' % (self.params.urlencode()) page_html_list.append(last_page) return ''.join(page_html_list) #直接返回整个字符串
二:stites 中的内容大整改 ,(把所有展示数据单独建立一个类)
原:数据内容
header_list = [] # 定制一个空别表 for field_or_func in self.get_new_list_display(): # 如果是多对多的 if callable(field_or_func): val = field_or_func(self, header=True) header_list.append(val) # header_list.append(field_or_func.__name__) # 如果是普通属性 else: if field_or_func == "__str__": val = self.model._meta.model_name.upper() else: field_obj = self.model._meta.get_field(field_or_func) val = field_obj.verbose_name # 自定义属性名 header_list.append(val) # 构建展示数据 new_data = [] for obj in queryset: temp = [] for field_or_func in self.get_new_list_display(): if callable(field_or_func): val = field_or_func(self, obj) # 获取函数返回值,传入obj进行从操作数据 elif not field_or_func == "__str__": from django.db.models.fields.related import ManyToManyField field_obj = self.model._meta.get_field(field_or_func) # 获取模型对象 # 判断是否为多对多属性 if type(field_obj) == ManyToManyField: raise Exception("list_display不能是多对多字段!") # 判断是否有选择属性 if field_obj.choices: val = getattr(obj, "get_%s_display" % field_or_func)() # 调用这个方法,反射方法,调用方法获取对应的内容 else: val = getattr(obj, field_or_func) if field_or_func in self.list_display_links: val = mark_safe("<a href='%s'>%s</a>" % (self.get_change_url(obj), val)) else: val = getattr(obj, field_or_func)() temp.append(val) # print(">>>>>>>>>>>>",temp) new_data.append(temp)
今:整改部分
1 :创建一个类
在原来的地方通过实例对象调用内容
2:函数说明
3:调用说明
requset ,当前操作请求 对应requset
self ,当前操作自定义配置类, 对应config_obj
quseryset 当前操作数据对应 queryset
4:分页包调用以及配置
4>1知识点:
ruquset.GET.get("筛选内容",默认值=None)
即如果不写默认值则为空,这里写为1 即,没获取到时候为1
4:>2 创建配置每页个数接口
前提:设置接口
4>3:最后返回的是分好页的数据(通过切片 [索引:索引])
4>4:抬头部分
4>5:展示数据
5:前端做的修改
前端效果
# -*- coding: utf-8 -*- # @Time : 2019/4/13 11:03 # @Author : Endless-cloud # @Site : # @File : stites.py # @Software: PyCharm ''' ┏┓ ┏┓+ + ┏┛┻━━━┛┻┓ + + ┃ ┃ ┃ ━ ┃ ++ + + + ████━████ ┃+ ┃ ┃ + ┃ ┻ ┃ ┃ ┃ + + ┗━┓ ┏━┛ ┃ ┃ ┃ ┃ + + + + ┃ ┃ Code is far away from bug with the animal protecting ┃ ┃ + 神兽保佑,代码无bug ┃ ┃ ┃ ┃ + ┃ ┗━━━┓ + + ┃ ┣┓ ┃ ┏┛ ┗┓┓┏━┳┓┏┛ + + + + ┃┫┫ ┃┫┫ ┗┻┛ ┗┻┛+ + + + ''' from django.urls import path, re_path from app01.models import * from django.shortcuts import HttpResponse, render, redirect from django.utils.safestring import mark_safe from django.urls import reverse 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] return queryset def get_header(self): header_list = [] # 定制一个空别表 for field_or_func in self.config_obj.get_new_list_display(): # 替换成config_obj 代用,self 代表原自定义配置类 # 如果是多对多的 if callable(field_or_func): val = field_or_func(self, header=True) header_list.append(val) # header_list.append(field_or_func.__name__) # 如果是普通属性 else: if field_or_func == "__str__": val = self.config_obj.model._meta.model_name.upper() else: field_obj = self.config_obj.model._meta.get_field(field_or_func) val = field_obj.verbose_name # 自定义属性名 header_list.append(val) return header_list #f返回前端的内容 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(): if callable(field_or_func): val = field_or_func(self.config_obj, obj) # 获取函数返回值,传入obj进行从操作数据 # self 统一换成self.config_obj elif not field_or_func == "__str__": 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不能是多对多字段!") # 判断是否有选择属性 if field_obj.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: val = mark_safe("<a href='%s'>%s</a>" % (self.config_obj.get_change_url(obj), val)) else: val = getattr(obj, field_or_func)() temp.append(val) # print(">>>>>>>>>>>>",temp) new_data.append(temp) return new_data #返回前端数据部分 class ModelStark(object): ''' 默认配置类 ''' list_display = ("__str__",) list_display_links = [] # 设置默认为空 model_form_class = None # 设置默认为无 per_page_num=None #设置接口 search_fields = [] #创建search_fidels接口 search_val = None # 默认让search_val的值为none def __init__(self, model): self.model = model self.model_name = self.model._meta.model_name self.app_label = self.model._meta.app_label # 反向解析当前访问表的增删改查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, obj=None): # 反向解析当前表的删除的URL add_url = reverse("%s_%s_add" % (self.app_label, self.model_name)) return add_url def get_delete_url(self, obj): # 反向解析当前表的删除的URL delete_url = reverse("%s_%s_delete" % (self.app_label, self.model_name), args=(obj.pk,)) return delete_url def get_change_url(self, obj): # 反向解析当前表的change的URL change_url = reverse("%s_%s_change" % (self.app_label, self.model_name), args=(obj.pk,)) return change_url def get_new_list_display(self): temp = [] temp.extend(self.list_display) # 继承原来的列表内容 temp.append(ModelStark.show_editbtn) # 注意传过来的是属性 temp.append(ModelStark.show_delbtn) # 注意传过来的是属性 temp.insert(0, ModelStark.show_checkbox) # temp.insert(1,self.show_checkbox()) # 同上不加括号,把方法名加入到列表方便掉用 return temp def show_checkbox(self, obj=None, header=False): # 展示选择列 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): if header: return "编辑" return mark_safe("<a href='%s'>编辑</a>" % self.get_change_url(obj)) def get_search_condition(self, request): val = request.GET.get("q") # 获取name q的内容 from django.db.models import Q # 导入Q 包 .进行或操作 q = Q() # 实例一个Q 对象 if val:# 如果进行了查询操作 self.search_val = val q.connector = "or" # 更改q为或操作 for field in self.search_fields: # ["title","price"] # 从接口中拿字段 print(field) # queryset=queryset.filter(Q(title__contains=val)|Q(price__contains=val)) q.children.append((field + "__contains", val)) # 利用小q进行模糊查询,和拼接 # "__contains 模糊查询" 即 title__contains=val 查询 else: self.search_val = None return q def list_view(self, request): # print(self) # 当前访问模型表对应的配置类对象 # print(self.model) # 当前访问模型表 queryset = self.model.objects.all() print("+>>>>>>>", 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() # print("<<<<<<<<<<<",new_data) return render(request, "stark/list_view.html", locals()) # data = self.model.objects.all() # print(data) # print("-------",self.list_display) # data_list=[] # dict1={} # for obj in data: # lis =[] # for msg in self.list_display: # lis.append(getattr(obj,msg)) # data_list.append(lis) # print("jjjjjjjjj",data_list) # return render(request, 'stark/list_view.html', { # "data_list":data_list, # "list_display":self.list_display # }) def get_model_form(self): # 创建获取model_form 内容函数 from django.forms import ModelForm # 导入包 class BaseModelForm(ModelForm): # 创建modelform类,继承modelform class Meta: # 创建可调用内容 model = self.model # 导入当前操作模型对象 fields = "__all__" # 获取全部 return self.model_form_class or BaseModelForm # 如果有内容就传入内容,没有就走默认的Base的 # 有的时候需要重新写class BasemodelForm类并继承他 def add_view(self, request): # 视图函数add_view BaseModelForm = self.get_model_form() # 通过get_model_form运行获取类 if request.method == "GET": form_obj = BaseModelForm() # 实例化对象获取内容 return render(request, "stark/add_view.html", locals()) # 传到前端内容(属性) else: form_obj = BaseModelForm(request.POST) # 向渲染的模型类中加入数据 if form_obj.is_valid(): # 判断接收数据是否可用 form_obj.save() # 将数据保存到数据库 return redirect(self.get_list_url()) # 跳转 else: return render(request, "stark/add_view.html", locals()) def change_view(self, request, id): # 创建change_view路由 ,传入request,id,通过反向解析获取id BaseModelForm = self.get_model_form() # 通过方法获取modelform类 edit_obj = self.model.objects.filter(pk=id).first() # 通过id获取当前操作对象 if request.method == "GET": form_obj = BaseModelForm(instance=edit_obj) # 通过instance参数,进行控制为传入对象可以到前端进行渲染 return render(request, "stark/change_view.html", locals()) else: form_obj = BaseModelForm(request.POST, instance=edit_obj) # 接收前端内容,instance对象,是内容进行覆盖 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): # 接收反向解析传过来的id if request.method == "POST": self.model.objects.filter(pk=id).delete() # 接收id 删除主键 return redirect(self.get_list_url()) # 跳转 list_url = self.get_list_url() # 把展示界面传过去.方便用户取消 return render(request, "stark/delete_view.html", locals()) # 新建删除html @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): admin_class = admin_class or ModelStark self._registry[model] = admin_class(model) def get_urls(self): # 动态为注册的模型类创建增删改查URL temp = [] # {Book:ModelAdmin(Book),Publish:ModelAdmin(Publish)} for model, config_obj in self._registry.items(): # print("---->", model, config_obj) model_name = model._meta.model_name app_label = model._meta.app_label temp.append( path("%s/%s/" % (app_label, model_name), config_obj.get_urls) ) ''' path("stark/app01/book",self.list_view) path("stark/app01/book/add",self.add_view) path("stark/app01/publish",self.list_view) path("stark/app01/publish/add",self.add_view) ''' return temp @property def urls(self): return self.get_urls(), None, None site = StarkSite()
三:stark 中的search内容
知识点: django中的Q()使用
实例化Q 以后可以进行更改内容且 通过字符串进行修改
接口内容
注册接口内容
1 :前端代码内容
设置name=q 进行from 表单数据操作
通过if
进行判断如果有配置search_fidleds 接口则显示出搜索框,否则
不显示搜索框
2:stites 数据
_ _contains 进行数据模糊查询
3: 数据通过search 进行数据过滤
效果展示:
通过''阿'' 进行 搜索