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)

 

所有功能全部完成

 

posted @ 2019-04-17 20:12  pythonernoob  阅读(122)  评论(0)    收藏  举报