效果图:
分页部分代码:
# 1.分页处理 all_count = self.model_class.objects.all().count() query_params = request.GET.copy() # 深copy query_params._mutable = True # query_params默认不可修改 pager = Pagination( current_page=request.GET.get('page'), all_count=all_count, base_url=request.path_info, query_params=query_params, per_page_data=self.per_page_data, ) data_list = self.model_class.objects.all()[pager.start:pager.end] context = { 'data_list': data_list, 'header_list': header_list, 'body_list': body_list, 'pager': pager, }
一、分页功能
stark/utils/pagination.py
""" 分页器 """ class Pagination(object): def __init__(self, current_page, all_count, base_url, query_params, per_page_data=20, display_page_number=11): """ 分页初始化 :param current_page: 当前页码 :param all_count: 数据库总条数 :param base_url: 基础URL :param query_params: Querydict对象,内部含所有当前URL的原条件 :param per_page_data: 每页显示数据条数 :param display_page_number: 页面上最多显示的页码数量 """ self.base_url = base_url try: self.current_page = int(current_page) if self.current_page <= 0: raise Exception() except Exception as e: self.current_page = 1 self.all_count = all_count self.query_params = query_params self.per_page_data = per_page_data self.display_page_number = display_page_number real_page_number, remainder = divmod(self.all_count, self.per_page_data) if remainder != 0: real_page_number += 1 self.real_page_number = real_page_number half_page_number = int(self.display_page_number / 2) self.half_page_number = half_page_number @property def start(self): """ 数据获取值起始索引 :return: """ return (self.current_page - 1) * self.per_page_data @property def end(self): """ 数据获取值结束索引 :return: """ return self.current_page * self.per_page_data def page_html(self): """ 生成HTML页码 :return: """ # 如果数据真实页码数小于11,则显示真实页码数 if self.real_page_number < self.display_page_number: pager_start = 1 pager_end = self.real_page_number else: # 真实页码数超过11 if (self.current_page + self.half_page_number) > self.real_page_number: pager_start = self.real_page_number - self.display_page_number + 1 pager_end = self.real_page_number else: pager_start = self.current_page - self.half_page_number pager_end = self.current_page + self.half_page_number page_list = [] if self.current_page <= 1: prev_page = '<li><a href="#">上一页</a></li>' else: self.query_params['page'] = self.current_page - 1 prev_page = '<li><a href="%s?%s">上一页</a></li>' % (self.base_url, self.query_params.urlencode()) page_list.append(prev_page) for page_num in range(pager_start, pager_end + 1): self.query_params['page'] = page_num if self.current_page == page_num: show_page_num = '<li class="active"><a href="%s?%s">%s</a></li>' % ( self.base_url, self.query_params.urlencode(), page_num) else: show_page_num = '<li><a href="%s?%s">%s</a></li>' % ( self.base_url, self.query_params.urlencode(), page_num) page_list.append(show_page_num) if self.current_page == self.real_page_number: next_page = '<li><a href="#">下一页</a></li>' else: self.query_params['page'] = self.current_page + 1 next_page = '<li><a href="%s?%s">下一页</a></li>' % (self.base_url, self.query_params.urlencode()) page_list.append(next_page) page_str = ''.join(page_list) return page_str
二、Strak组件
stark/service/core_func.py
from types import FunctionType from django.urls import re_path from django.utils.safestring import mark_safe from django.shortcuts import HttpResponse, render, reverse from stark.utils.pagination import Pagination def get_choice_text(title, field): """ 对于Stark组件中定义列时,choice如果想要显示中文信息,调用此方法即可。 :param title: 希望页面显示的表头 :param field: 字段名称 :return: """ def inner(self, obj=None, is_header=None): if is_header: return title method = "get_%s_display" % field return getattr(obj, method)() # GENDER_CHOICES = ((MALE, '男'),(FEMALE, '女'),) # 对于choice字段,如果想获取获取第二个值,可以通过:对象.get_字段名_display() return inner class StarkHandler(object): list_display = [] per_page_data = 10 def __init__(self, site, model_class, prev): self.site = site self.model_class = model_class self.prev = prev def display_edit(self, obj=None, is_header=None): """ 自定义页面显示的列(表头和内容) :param obj: :param is_header: :return: """ if is_header: return '编辑' name = '%s:%s' % (self.site.namespace, self.get_edit_url_name,) return mark_safe('<a href="%s">编辑</a>' % reverse(name, args=(obj.pk,))) def display_delete(self, obj=None, is_header=None): if is_header: return '删除' name = '%s:%s' % (self.site.namespace, self.get_delete_url_name,) return mark_safe('<a href="%s">删除</a>' % reverse(name, args=(obj.pk,))) def get_list_display(self): """ 获取页面上应该显示的列,预留的自定义扩展,例如:以后根据用户的不同显示不同的列 :return: """ value = [] value.extend(self.list_display) return value def list_view(self, request): """ 列表页面 :param request: :return: """ # 1.分页处理 all_count = self.model_class.objects.all().count() query_params = request.GET.copy() # 深copy query_params._mutable = True # query_params默认不可修改 pager = Pagination( current_page=request.GET.get('page'), all_count=all_count, base_url=request.path_info, query_params=query_params, per_page_data=self.per_page_data, ) data_list = self.model_class.objects.all()[pager.start:pager.end] list_display = self.get_list_display() # 会优先调用UserInfoHandler里的get_list_display()方法。 # 2.1 处理表格的表头 header_list = [] if list_display: for field_or_func in list_display: if isinstance(field_or_func, FunctionType): verbose_name = field_or_func(self, obj=None, is_header=True) else: verbose_name = self.model_class._meta.get_field(field_or_func).verbose_name header_list.append(verbose_name) else: header_list.append(self.model_class._meta.model_name) # 如果用户没有填写list_display,就显示表名 # 2.2 处理表的内容 body_list = [] for obj in data_list: tr_list = [] if list_display: for field_or_func in list_display: if isinstance(field_or_func, FunctionType): tr_list.append(field_or_func(self, obj, is_header=False)) else: tr_list.append(getattr(obj, field_or_func)) else: tr_list.append(obj) # 如果用户没有填写list_display,就显示表对象,所以表类要定义__str__方法 body_list.append(tr_list) context = { 'data_list': data_list, 'header_list': header_list, 'body_list': body_list, 'pager': pager, } return render(request, 'stark/data_list.html', context) def add_view(self, request): """ 添加页面 :param request: :return: """ return HttpResponse('添加页面') def edit_view(self, request, pk): """ 编辑页面 :param request: :return: """ return HttpResponse('编辑页面') def delete_view(self, request, pk): """ 删除页面 :param request: :param pk: :return: """ return HttpResponse('删除页面') def get_url_name(self, params): app_label, model_name = self.model_class._meta.app_label, self.model_class._meta.model_name if self.prev: return '%s_%s_%s_%s' % (app_label, model_name, self.prev, params) return '%s_%s_%s' % (app_label, model_name, params) @property def get_list_url_name(self): """ 获取列表页面URL的name :return: """ return self.get_url_name('list') @property def get_add_url_name(self): """ 获取添加页面URL的name :return: """ return self.get_url_name('add') @property def get_edit_url_name(self): """ 获取编辑页面URL的name :return: """ return self.get_url_name('edit') @property def get_delete_url_name(self): """ 获取删除页面URL的name :return: """ return self.get_url_name('delete') def get_urls(self): patterns = [ re_path(r'^list/$', self.list_view, name=self.get_list_url_name), re_path(r'^add/$', self.add_view, name=self.get_add_url_name), re_path(r'^edit/(d+)/$', self.edit_view, name=self.get_edit_url_name), re_path(r'^delete/(d+)/$', self.delete_view, name=self.get_delete_url_name), ] patterns.extend(self.extra_urls()) return patterns def extra_urls(self): return [] class StarkSite(object): def __init__(self): self._registry = [] self.app_name = 'stark' self.namespace = 'stark' def register(self, model_class, handler_class=None, prev=None): """ :param model_class: 是models中的数据库表对应的类。 :param handler_class: 处理请求的视图函数所在的类 :param prev: 生成URL的前缀 :return: """ if not handler_class: handler_class = StarkHandler self._registry.append( {'model_class': model_class, 'handler': handler_class(self, model_class, prev), 'prev': prev}) def get_urls(self): patterns = [] for item in self._registry: model_class = item['model_class'] handler = item['handler'] prev = item['prev'] app_name, model_name = model_class._meta.app_label, model_class._meta.model_name if prev: patterns.append( re_path(r'^%s/%s/%s/' % (app_name, model_name, prev,), (handler.get_urls(), None, None))) else: patterns.append(re_path(r'^%s/%s/' % (app_name, model_name,), (handler.get_urls(), None, None))) return patterns @property def urls(self): return self.get_urls(), self.app_name, self.namespace site = StarkSite()
三、业务处理
web/stark.py
from stark.service.core_func import site, StarkHandler, get_choice_text from web import models class DepartmentHandler(StarkHandler): list_display = ['title'] class UserInfoHandler(StarkHandler): per_page_data = 1 list_display = [ 'name', get_choice_text('性别', 'gender'), get_choice_text('班级', 'classes'), 'age', 'email', 'department', StarkHandler.display_edit, StarkHandler.display_delete, ] site.register(models.Department, DepartmentHandler) # 给部门的url增加了前缀:/stark/web/department/private/ site.register(models.UserInfo, UserInfoHandler)
四、模板渲染
{% extends 'layout.html' %} {% block content %} <div class="custom-container"> <table class="table table-bordered"> <thead> <tr> {% for item in header_list %} <th>{{ item }}</th> {% endfor %} </tr> </thead> <tbody> {% for row in body_list %} <tr> {% for ele in row %} <td>{{ ele }}</td> {% endfor %} </tr> {% endfor %} </tbody> </table> <!-- 分页 --> <nav> <ul class="pagination"> {{ pager.page_html|safe }} </ul> </nav> <!-- 分页结束 --> </div> {% endblock content %}