DC's blog DC's blog
首页
  • 计算机基础
  • linux基础
  • mysql
  • git
  • 数据结构与算法
  • axure
  • english
  • docker
  • opp
  • oop
  • 网络并发编程
  • 不基础的py基础
  • 设计模式
  • html
  • css
  • javascript
  • jquery
  • UI
  • 第一次学vue
  • 第二次学vue
  • Django
  • drf
  • drf_re
  • 温故知新
  • flask
  • 前后端不分离

    • BBS
    • 订单系统
    • CRM
  • 前后端部分分离

    • pear-admin-flask
    • pear-admin-django
  • 前后端分离

    • 供应链系统
  • 理论基础
  • py数据分析包
  • 机器学习
  • 深度学习
  • 华中科大的网课
  • cursor
  • deepseek
  • 杂文
  • 罗老师语录
  • 关于我

    • me
  • 分类
  • 归档
GitHub (opens new window)

DC

愿我一生欢喜,不为世俗所及.
首页
  • 计算机基础
  • linux基础
  • mysql
  • git
  • 数据结构与算法
  • axure
  • english
  • docker
  • opp
  • oop
  • 网络并发编程
  • 不基础的py基础
  • 设计模式
  • html
  • css
  • javascript
  • jquery
  • UI
  • 第一次学vue
  • 第二次学vue
  • Django
  • drf
  • drf_re
  • 温故知新
  • flask
  • 前后端不分离

    • BBS
    • 订单系统
    • CRM
  • 前后端部分分离

    • pear-admin-flask
    • pear-admin-django
  • 前后端分离

    • 供应链系统
  • 理论基础
  • py数据分析包
  • 机器学习
  • 深度学习
  • 华中科大的网课
  • cursor
  • deepseek
  • 杂文
  • 罗老师语录
  • 关于我

    • me
  • 分类
  • 归档
GitHub (opens new window)
  • BBS

  • 订单平台

    • 单点知识
    • 表结构
    • 用户名登陆
    • 短信登陆
    • 菜单和权限
    • 级别管理
    • 客户管理
    • 分页和搜索
      • 创建模拟数据
      • 分页(初始版)
      • 分页(封装版)
        • QueryDict类型
        • 分页组件
      • 搜索
    • 价格策略
    • 交易中心
    • message组件
    • 我的交易列表
    • worker
    • 部署之代码同步
    • 部署之线上运行
    • 张sir的部署
  • CRM

  • flask+layui

  • django+layui

  • 供应链

  • 实战
  • 订单平台
DC
2023-07-12
目录

分页和搜索

# 创建模拟数据

用离线脚本创建多条客户数据

import os
import sys
import django

base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(base_dir)

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'order.settings')
django.setup()

if __name__ == '__main__':
    from web import models
    from utils.encrypt import md5
    
    for i in range(1, 302):
        models.Customer.objects.create(
            username=f'dc_{i}',
            password=md5("dc1234"),
            mobile='18954787888',
            level_id=1,
            creator_id=1
        )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# 分页(初始版)

image-20230410095230719

1.一开始,在展示客户列表数据时,返回的是所有的客户数据.300多条数据一起展示在页面上,是不合理的,需要分页.
  构造类似于 http://127.0.0.1:8000/customer/list/?page=2 的url. 
  后端通过request.GET.get("page")得知,需查询第2页的数据,可以计算出开始和结束的位置,将查询的所有数据进行分片. 
  ★ queryset对象是可以分片的!! 访问第一页: 0-10; 第二页:10-20; 第三页: 20-30; (默认每页数据是10条)
    So, start=(page-1)*10  end=page*10
2.运用bootstrap中的分页组件.
1
2
3
4
5
6

关键代码如下:
web/views/customer.py

def customer_list(request):
    """ 获取客户数据 分页 """
    page = request.GET.get("page")  # 当前页
    page = int(page)
    per_page_count = 10  # 每页显示的个数
    start = (page - 1) * per_page_count  # 分片开始位置
    end = page * per_page_count  # 分片结束位置
    queryset = models.Customer.objects.filter(active=1).select_related('level', 'creator')  # 所有客户数据
    total_count = queryset.count()  # 总数据条数
    total_page, mod = divmod(total_count, per_page_count)  # 计算出总共有多少页
    if mod:
        total_page += 1

    # 总页码小于11,直接全部显示
    if total_page <= 11:
        start_page = 1
        end_page = total_page
    else:
        # 总页码比较多
        if page <= 6:  # 若当前页<=6 "左极值", 显示1~11
            start_page = 1
            end_page = 11
        else:
            if (page + 5) > total_page:  # "右极值"
                start_page = total_page - 10
                end_page = total_page
            else:  # 中间
                start_page = page - 5
                end_page = page + 5
                
    # 前端页面页码的生成逻辑
    page_list = []
    for i in range(start_page, end_page + 1):
        if i == page:
            item = f"<li class='active'><a href='?page={i}'>{i}</a></li>"
        else:
            item = f"<li><a href='?page={i}'>{i}</a></li>"
        page_list.append(item)
    pager_string = "".join(page_list)

    context = {
        "queryset": queryset[start:end],
        "pager_string": mark_safe(pager_string),
    }
    return render(request, 'customer_list.html', context)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45

web/templates/customer_list.html

    <nav aria-label="Page navigation">
        <ul class="pagination">
            {{ pager_string }}
        </ul>
    </nav>
1
2
3
4
5

# 分页(封装版)

将分页的代码进行封装, 并进行一系列的优化!!

# QueryDict类型

用于后面分页组件 对原搜索条件的 保留!!

http://127.0.0.1:8000/customer/list/?filter=wupeiqi&age=19

request.GET对象 是 QueryDict类型 的数据!!
1.默认QueryDict不允许被修改 _mutable=False
	设置request.GET._mutable = True后,就可以被修改啦!
  
2.设置值
  print(request.GET)  # <QueryDict: {'filter': ['wupeiqi'], 'age': ['19']}>
  request.GET.setlist("name",[123])
  print(request)  # <QueryDict: {'filter': ['wupeiqi'], 'age': ['19'], 'name': ['123']}>
  request.GET.setlist("name",[123,321])
  print(request)  # <QueryDict: {'filter': ['wupeiqi'], 'age': ['19'], 'name': ['123','321']}>

3.调用urlencode方法,可以拼接
  import copy
  query_dict = copy.deepcopy(request.GET)  # 避免污染原本的request.GET!!
  query_dict._mutable = True
  query_dict.setlist("name",[123]) 
  paramString = query_dict.urlencode()
  print(paramString)  # "filter=wupeiqi&age=19&name=123"
  query_dict.setlist("name",[123,321]) 
  paramString = query_dict.urlencode()
  print(paramString)  # "filter=wupeiqi&age=19&name=123&name=321"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# 分页组件

可以不影响url已携带的参数,并在url中加上分页的参数!!!

image-20230410124047514

"""
分页组件的使用,需要以下两个步骤:
-- 视图函数:
    def customer_list(request):
        queryset = models.Customer.objects.filter(active=1).select_related('level')  # 查询所有数据

        obj = Pagination(request, queryset)  # 使用分页组件对查询出的所有数据进行分页
        context = {
            "queryset": obj.current_page_queryset,
            "pager_string": obj.html,
        }
        return render(request, 'customer_list.html', context)
        # ★ V2版本,在视图函数中传入pager对象,在模版中调用 pager.current_page_queryset 以及pager.html
        # pager = Pagination(request, queryset)
        # return render(request, 'customer_list.html', {"pager": pager})

-- 前端页面:
    {% for row in queryset %}  <!-- {% for row in  pager.current_page_queryset %} -->
        {{row.id}}
        {{row.username}}
    {% endfor %}

    <nav aria-label="Page navigation">
        <ul class="pagination">
            {{ pager_string }}  <!-- {{ pager.html }} -->
        </ul>
    </nav>
"""
import copy
from django.utils.safestring import mark_safe


class Pagination(object):
    """ 分页 """

    def __init__(self, request, query_set, per_page_count=10, pager_count=8):

        self.query_dict = copy.deepcopy(request.GET)
        self.query_dict._mutable = True

        self.query_set = query_set  # 要进行分页的所有数据(是一个queryset对象)
        self.per_page_count = per_page_count  # 每页显示的条数
        self.pager_count = pager_count  # 分页栏中最多显示的页码个数
        self.total_count = query_set.count()  # 一共多少条数据
        self.pager_count_half = pager_count // 2  # 当前页向前向后展示的页码个数

        self.total_page, div = divmod(self.total_count, per_page_count)
        if div:
            self.total_page += 1  # 计算出总共有多少页/最大的页码数 使用内置函数divmod

        self.page = self.check_page(request)  # 当前页
        self.start = (self.page - 1) * per_page_count  # 切片的开始位置
        self.end = self.page * per_page_count  # 切片的结束位置

    def check_page(self, request):
        """对url中携带的page参数进行判断!!"""
        page = request.GET.get('page')
        if not page:
            page = 1
        else:
            if not page.isdecimal():
                page = 1
            else:
                page = int(page)
                if page <= 0:
                    page = 1
                else:
                    if page > self.total_page:
                        page = self.total_page
        return page

    def page_nav(self):
        """分页栏中的开始页码和结束页码"""
        # 总页码小于11 (分页栏最多显示的页码个数默认是11)
        if self.total_page <= self.pager_count:
            start_page = 1
            end_page = self.total_page
        # 总页码大于11
        else:
            # 当前页如果<=11//2 "左极值 避免开始页码<=0"
            if self.page <= self.pager_count_half:
                start_page = 1
                end_page = self.pager_count
            else:
                # 当前页如果>11//2 "右极值 避免结束页码>总页码"
                if (self.page + self.pager_count_half) > self.total_page:
                    start_page = self.total_page - self.pager_count + 1
                    end_page = self.total_page
                else:
                    start_page = self.page - self.pager_count_half
                    end_page = self.page + self.pager_count_half
        return start_page, end_page

    @property
    def html(self):
        """前端分页栏代码(前端使用的是Bootstrap!)"""
        pager_list = []
        # 无数据,分页栏不展示
        if not self.total_page:
            return ""

        # start_page、end_page 分页栏开始和结束页码
        start_page, end_page = self.page_nav()

        # 分页栏首页html
        self.query_dict.setlist('page', [1])
        pager_list.append('<li><a href="?{}">首页</a></li>'.format(self.query_dict.urlencode()))

        # 分页栏上一页html
        if self.page == 1:
            pager_list.append('<li class="disabled"><a href="#">上一页</a></li>')
        if self.page > 1:
            self.query_dict.setlist('page', [self.page - 1])
            pager_list.append('<li><a href="?{}">上一页</a></li>'.format(self.query_dict.urlencode()))

        # 分页栏中间页html
        for i in range(start_page, end_page + 1):
            self.query_dict.setlist('page', [i])
            if i == self.page:
                item = '<li class="active"><a href="?{}">{}</a></li>'.format(self.query_dict.urlencode(), i)
            else:
                item = '<li><a href="?{}">{}</a></li>'.format(self.query_dict.urlencode(), i)
            pager_list.append(item)

        # 分页栏下一页html
        if self.page == self.total_page:
            pager_list.append('<li class="disabled"><a href="#">下一页</a></li>')
        if self.page < self.total_page:
            self.query_dict.setlist('page', [self.page + 1])
            pager_list.append('<li><a href="?{}">下一页</a></li>'.format(self.query_dict.urlencode()))

        # 分页栏尾页html
        self.query_dict.setlist('page', [self.total_page])
        pager_list.append('<li><a href="?{}">尾页</a></li>'.format(self.query_dict.urlencode()))

        # 分页栏汇总
        pager_list.append('<li class="disabled"><a>共{}页/{}条</a></li>'.format(
            self.total_page,
            self.total_count)
        )
        pager_string = mark_safe("".join(pager_list))
        return pager_string

    @property
    def current_page_queryset(self):
        """当前页的数据"""
        if self.total_count:
            return self.query_set[self.start:self.end]
        return self.query_set
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149

# 搜索

点击搜索按钮, 以get的请求方式将搜索的关键字提交到当前的url中.

image-20230410144926956

前端关键代码

<div style="margin-bottom: 10px" class="clearfix"> <!-- 注意:class="clearfix" -->
    <a class="btn btn-success" href="{% url 'customer_add' %}">
        <span class="glyphicon glyphicon-plus-sign"></span>
        新建
    </a>
    <form class="navbar-form navbar-right" role="search" method="get">
        <div class="form-group">
        <input name="keyword" type="text" class="form-control" placeholder="请输入关键字" value="{{ keyword }}">
        </div>
        <button type="submit" class="btn btn-default">
            <span class="glyphicon glyphicon-search"></span>
        </button>
    </form>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14

后端关键代码

def customer_list(request):
    """ 获取客户数据 分页、搜索 """
    keyword = request.GET.get("keyword", "").strip()
    con = Q()
    if keyword:
        # 名字 or 手机号 or 级别的标题中有关键字
        con.connector = 'OR'
        con.children.append(('username__contains', keyword))
        con.children.append(('mobile__contains', keyword))
        con.children.append(('level__title__contains', keyword))

    queryset = models.Customer.objects.filter(con).filter(active=1).select_related('level', 'creator')
    obj = Pagination(request, queryset)
    context = {
        "queryset": obj.current_page_queryset,
        "pager_string": obj.html,
        "keyword": keyword,
    }
    return render(request, 'customer_list.html', context)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

思考问题:
1> 编辑回来携带条件: 分页后, 加入点击第19页, 修改了某一条数据, 修改后返回的页面, 自动展示的是第一页的数据.. 不合理.
2> 权限控制按钮是否出现: 将权限的粒度控制在显示页面的按钮上.


客户管理
价格策略

← 客户管理 价格策略→

最近更新
01
deepseek本地部署+知识库
02-17
02
实操-微信小程序
02-14
03
教学-cursor深度探讨
02-13
更多文章>
Theme by Vdoing | Copyright © 2023-2025 DC | One Piece
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式