【Django 网页Web开发】12. 实战项目:分页组件的封装 面向接口编程(05)(保姆级图文)


    1. 对象的方式使用分页组件

    自定义的分页组件,以后如果想要使用这个分页组件,你需要做如下几件事:

    在视图函数中:
    def pretty_list(request):

        # 1.根据自己的情况去筛选自己的数据
        queryset = models.PrettyNum.objects.all()
    
        # 2.实例化分页对象
        page_object = Pagination(request, queryset)
    
        context = {
            "queryset": page_object.page_queryset,  # 分完页的数据
            "page_string": page_object.html()       # 生成页码
        }
        return render(request, 'pretty_list.html', context)

    在HTML页面中

    {% for obj in queryset %}
        {{obj.xx}}
    {% endfor %}
    
    <ul class="pagination">
        {{ page_string }}
    </ul>

    2. 项目结构

    在app01下新建utils文件夹,新建pagination.py文件

    1269003670ba4ed6b5a19e6baf41a01c.png

    3. 编写pagination.py

           首先在def __init__获取得到request 和 request, queryset, page_size=10,page_param=“page”, plus=5 等必要数据给必要的数据标注self(成为对象的属性,便于后面对象方法的调用)创建html()函数用于返回生成的html页码相关的代码,可以调用前面定义的self属性上述的大部分操作就是把原来的代码cv到pagination.py,给必要的属性前加上self.

    """
    自定义的分页组件,以后如果想要使用这个分页组件,你需要做如下几件事:
    
    在视图函数中:
        def pretty_list(request):
    
            # 1.根据自己的情况去筛选自己的数据
            queryset = models.PrettyNum.objects.all()
    
            # 2.实例化分页对象
            page_object = Pagination(request, queryset)
    
            context = {
                "queryset": page_object.page_queryset,  # 分完页的数据
                "page_string": page_object.html()       # 生成页码
            }
            return render(request, 'pretty_list.html', context)
    
    在HTML页面中
    
        {% for obj in queryset %}
            {{obj.xx}}
        {% endfor %}
    
        <ul class="pagination">
            {{ page_string }}
        </ul>
    
    """
    import math
    
    from django.shortcuts import render
    from django.utils.safestring import mark_safe
    class Pagination(object):
    
        def __init__(self, request, queryset, page_size=10, page_param="page", plus=5):
            """
            :param request: 请求的对象
            :param queryset: 符合条件的数据(根据这个数据给他进行分页处理)
            :param page_size: 每页显示多少条数据
            :param page_param: 在URL中传递的获取分页的参数,例如:/etty/list/?page=12
            :param plus: 显示当前页的 前或后几页(页码)
            """
    
            import copy
            query_dict = copy.deepcopy(request.GET)#复制query
            query_dict._mutable = True #设置query可以修改
            self.query_dict = query_dict
            self.page_param = page_param # 获取分页的参数,在分页组件中是 "page"
    
            page = request.GET.get(page_param, "1")
            #默认值是字符串的1是为了能够支持更多可能的数据类型,
            #可能以后我们封装数据接口的时候不一定是数据类型,
            #所以要把转化为int的工作放在下面的代码中
    
            if page.isdecimal():
                page = int(page)#转化为int类型
            else:
                page = 1
    
            self.page = page
            self.page_size = page_size # 每一页记录个数
    
            self.start = (page - 1) * page_size # 记录开始的区间
            self.end = page * page_size # 记录结束的区间
    
            self.page_queryset = queryset[self.start:self.end] #获取当前页码的数据
    
            #total_count = models.PrettyNum.objects.all().count()  # 统计所有数据记录数
            total_count = queryset.count()# 统计所有数据记录数
    
            # 页码
    
            self.plus = 5  # 当前页面 前后几页的数值
            self.total_page= math.ceil(total_count / page_size)  # 总共页面数
    
        def html(self):
            page_str_list = []
            if self.total_page < 2 * self.plus + 1:  # 页面过少,起始和结束页码改为1和总页面数
                start_page = 1
                end_page = self.total_page
            elif self.page < 1 + self.plus:  # 避免出现负数页码
                start_page = 1
                end_page = self.page + self.plus
            elif self.page + self.plus > self.total_page:  # 避免超过总页数
                start_page = self.page - self.plus
                end_page = self.total_page  # +1是因为range闭区间
            else:
                start_page = self.page - self.plus  # 当前页面
                end_page = self.page + self.plus
            # 首页
            self.query_dict.setlist(self.page_param, [1])
            page_str_list.append('<li><a href="?{}">首页</a></li>'.format(self.query_dict.urlencode()))
    
            # 上一页
            if self.page <= 1:
                self.query_dict.setlist(self.page_param, [1])
                prev = '<li><a href="?{}">上一页</a></li>'.format(self.query_dict.urlencode())
            else:
                self.query_dict.setlist(self.page_param, [self.page - 1])
                prev = '<li><a href="?{}">上一页</a></li>'.format(self.query_dict.urlencode())
            page_str_list.append(prev)
    
            # for i in range(1, 21):
            for i in range(start_page, end_page + 1):  # (306/10)=30.6->向上取整31 ->因为range右边闭区间还要+1
                if i == self.page:  # 如果是当前所在页面,添加激活样式
                    self.query_dict.setlist(self.page_param, [i])
                    ele = '<li class="active"><a href="?{}">{}</a></li>'.format(self.query_dict.urlencode(),i)  # 生成多行页码li
                else:
                    self.query_dict.setlist(self.page_param, [i])
                    ele = '<li><a href="?{}">{}</a></li>'.format(self.query_dict.urlencode(),i)  # 生成多行页码li
                page_str_list.append(ele)  # 添加到一个列表中
    
            # 下一页
            if self.page < self.total_page:  # 判断是否超过总页数
                self.query_dict.setlist(self.page_param, [self.page + 1])
                next = '<li><a href="?{}">下一页</a></li>'.format(self.query_dict.urlencode())
            else:
                self.query_dict.setlist(self.page_param, [self.total_page])
                next = '<li><a href="?{}">下一页</a></li>'.format(self.query_dict.urlencode())
            page_str_list.append(next)
            # 尾页
            self.query_dict.setlist(self.page_param, [self.total_page])
            page_str_list.append('<li><a href="?{}">尾页</a></li>'.format(self.query_dict.urlencode()))
    
            # 分页搜索框
            search_string = '''
                 <form method="get">
                <div class="input-group" style="width:200px">
                <input type="text" name="page" class="form-control" value="" placeholder="页码">
                <span class="input-group-btn">
                <button class="btn btn-default" type="submit">跳转</button></span>
                        </div>
                    </form>
                '''
            page_str_list.append(search_string)
    
            # page_string = "".join(page_str_list)  # 用""分隔符将列表页码的代码连接形成的字符串,需要加个安全信任才能变成html
            page_string = mark_safe("".join(page_str_list))  # 用""分隔符将列表页码的代码连接形成的字符串,需要加个安全信任才能变成html
            return page_string

    3.2 view.py

    以后其他页面使用分页功能也只需要page_object = Pagination(request, queryset)即可

    def pretty_list(request):
        """ 靓号列表 """
    
        ## 随机创建若干数据,写好后f5刷新一下列表生效后注释掉这些代码
        # for i in range(300):
        #     models.PrettyNum.objects.create(mobile=random.randint(13000000000,19000000000),price=random.randint(1,1000),level=random.randint(1,4),status=random.randint(1,2))
    
    
        data_dict = {}
        search_data = request.GET.get("q")
        if search_data:  # 如果传来的参数非空就搜索,反之显示所有数据
            data_dict["mobile__contains"] = search_data  # 是否包含指定数据
    
        queryset = models.PrettyNum.objects.filter(**data_dict).order_by("-level")  # -level表示按照level降序排列显示
    
        page_object = Pagination(request, queryset)
        context = {
            "search_data": search_data,
            "queryset": page_object.page_queryset,  # 分完页的数据
            "page_string": page_object.html()  # 页码
        }
        return render(request, 'pretty_list.html', context)

    2.png

    4. bug修改之:url中搜索关键词q和page

    我们如何做到同时具有搜索功能和分页功能呢?
    分页时候,保留原来的搜索条件

    http://127.0.0.1:8000/pretty/list/?q=888
    http://127.0.0.1:8000/pretty/list/?page=1
    
    http://127.0.0.1:8000/pretty/list/?q=888&page=23

    现在的关键在于如何在代码中实现?q=888&page=23


    4.1 构造url的一个雏形

    在原来的基础上增加了&page=11

    def pretty_list(request):
        import copy
        query_dict=copy.deepcopy(request.GET)#http://127.0.0.1:8000/pretty/list/?q=1&csdn=2023
        print(query_dict.urlencode())#q=1&csdn=2023
        query_dict.mutable=True#不修改这个参数就会报错提示无法修改
        query_dict.setlist('page',[11])#q=1&csdn=2023&page=11 在原来的基础上增加了&page=11
        print(query_dict.urlencode())#讲url打印出来

    3.png

    4.2 修改我们的分页组件

    pagination.py核心修改代码1 加入self.query_dict:

        def __init__(self, request, queryset, page_size=10, page_param="page", plus=5):
            import copy
            query_dict = copy.deepcopy(request.GET)#复制query
            query_dict._mutable = True #设置query可以修改
            self.query_dict = query_dict

    pagination.py核心修改代码2 把所有page换成参数列表self.query_dict,相当于原来只用page这个参数,现在是page和搜索关键词或者更多参数的一个参数列表:
    以首页例子,就是把原来format的内容给到self中设置,然后再用self.query_dict.urlencode()替换原来format中的内容

            # 首页
            page_str_list.append('<li><a href="?page={}">首页</a></li>'.format(1))
            # 修改后的首页
            self.query_dict.setlist(self.page_param, [1])
            page_str_list.append('<li><a href="?{}">首页</a></li>'.format(self.query_dict.urlencode()))

    4.3 搜索小bug

    搜索框出现None
    原因:没有给搜索关键词q设置默认值

    search_data = request.GET.get("q")
    # 将上一行替换为下面的代码即可
    search_data = request.GET.get("q","")

    5. 应用分页组件,几行代码实现用户管理分页

    5.1 批量创建用户,增加用户数量

    因为用户表有个创建时间,会有时区问题,需要先修改setting.py中的设置
    Django项目中设置USE_TZ=False

    4.png

    for i in range(300):
        models.UserInfo.objects.create(name="张龙分身{}".format(i),password="123456",age=1,account=1,gender=1,create_time="2023-03-15 10:00:17.000000", depart_id=1)

    5.2 修改user_list.html

    放入

            <ul class="pagination">
                {{ page_string }}
            </ul>

    完整代码:

    {% extends 'layout.html' %}
    
    {% block content %}
        <div class="container">
        <div style="margin-bottom: 10px" class="clearfix">
            <a class="btn btn-success" href="/pretty/add/">
                <span class="glyphicon glyphicon-plus-sign" aria-hidden="true"></span>
                新建靓号
            </a>
            <div style="float: right;width: 300px;">
                <form method="get">
                    <div class="input-group">
                        <input type="text" name="q" class="form-control" placeholder="Search for..."
                               value="{{ search_data }}">
                        <span class="input-group-btn">
                            <button class="btn btn-default" type="submit">
                                <span class="glyphicon glyphicon-search" aria-hidden="true"></span>
                            </button>
                          </span>
                    </div>
                </form>
            </div>
    
        </div>
        <div class="panel panel-default">
            <!-- Default panel contents -->
            <div class="panel-heading">
                <span class="glyphicon glyphicon-th-list" aria-hidden="true"></span>
                靓号列表
            </div>
    
            <!-- Table -->
            <table class="table table-bordered">
                <thead>
                <tr>
                    <th>ID</th>
                    <th>号码</th>
                    <th>价格</th>
                    <th>级别</th>
                    <th>状态</th>
                    <th>操作</th>
                </tr>
                </thead>
                <tbody>
                {% for obj in queryset %}
                    <tr>
                        <th>{{ obj.id }}</th>
                        <td>{{ obj.mobile }}</td>
                        <td>{{ obj.price }}</td>
                        <td>{{ obj.get_level_display }}</td>
                        <td>{{ obj.get_status_display }}</td>
                        <td>
                            <a class="btn btn-primary btn-xs" href="/pretty/{{ obj.id }}/edit/">编辑</a>
                            <a class="btn btn-danger btn-xs" href="/pretty/{{ obj.id }}/delete/">删除</a>
                        </td>
                    </tr>
                {% endfor %}
    
                </tbody>
    
            </table>
    
    
        </div>
        <div class="clearfix">
            <ul class="pagination">
                {{ page_string }}
            </ul>
        </div>
        </div>
    
    
    {% endblock %}

    5.3 修改view.py

    def user_list(request):
        """ 用户管理 """
        # 获取所有用户列表 [obj,obj,obj]
        queryset = models.UserInfo.objects.all()
    
        page_object = Pagination(request, queryset, page_size=5)
        context = {
            "queryset": page_object.page_queryset,
            "page_string": page_object.html(),
        }
        return render(request, 'user_list.html', context)

    5.png



    6 查看全部:

    应本人需求,偶尔需要请求显示 所需的全部数据

    views.py

    search_all_data = request.GET.get("search_all_data", 10)
    obj = obj.objects.all()
    
    page_object = Pagination(request, queryset, page_size=int(search_all_data))

    安需求加入或修改以上2行代码

    html中

    <div class="clearfix">

        <ul class="pagination">

            <button type="submit" class="btn" onclick="dataBtn('{{ ojc.count }}')">全部</button>

            {{ page_string }}
        </ul>
    </div>

    js添加点击事件

    <script>
        function dataBtn(data) {
            var on_url = window.location.search
            if (on_url === '') {
                var url = "{% url 'sendorder' %}" + "?search_all_data=" + data
            } else {
                var url = "{% url 'sendorder' %}" + on_url + "&search_all_data=" + data
            }
    
            $.ajax({
                url: url,
                type: 'get',
                data: '',
                success: function (result) {
                    window.location.replace(url)
                },
                error: function (error) {
                },
            })
        }
    </script>

    // 如果无需判断,可直接省去;具体看个人需求

    <script>
        function dataBtn(data) {
            var url = "{% url 'sendorder' %}" + "?search_all_data=" + data
            $.ajax({
                url: url,
                type: 'get',
                data: '',
                success: function (result) {
                    window.location.replace(url)
                },
                error: function (error) {
                },
            })
        }
    </script>