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

  • 订单平台

  • CRM

    • rbac

    • stark

      • 自动生成
      • CURD
      • 搜索相关
        • 排序
        • 关键字搜索
        • 批量操作
        • 组合搜索框
          • 搜索条件封装和扩展
          • 搜索基本展示
          • 封装统一对象
          • 文本显示自定制
          • 组合搜索框的样式
        • 组合搜索实现
          • 为按钮生成url
          • 组合搜索-单选
          • 组合搜索-多选
          • 进行筛选
        • ※ 后续: 数据过滤
        • ※ 后续: CURD的页面自定制
        • ※ 后续: 反向生成url的优化!
        • ◎ BUG
        • 附录
    • crm

  • flask+layui

  • django+layui

  • 供应链

  • 实战
  • CRM
  • stark
DC
2024-03-22
目录

搜索相关

# 排序

可以根据我们的心情, 指定排序规则.

排序规则: eg - ["name","-age"] 先按照name正序排序,name相同的按照年龄从大到小倒序排序..

image-20240325033714537

准备一点数据

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', 'dc_stark.settings')
django.setup()

if __name__ == '__main__':
    from apps.app01 import models

    for i in range(1, 302):
        models.UserInfo.objects.create(
            name=f'dc_{i}',
            age=10,
            email=f'18954787{i}@qq.com',
            depart_id=1,
        )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 关键字搜索

Q对象的应用场景: 关键字搜索!!

先来看看效果!!

44

★实现思路:
在页面里设置搜索表单,点击搜索按钮,以GET的方式(会在url中添加param参数) 提交到当前url!
后台通过 request.GET.get("input搜索框name属性值", "") 获取到关键字后, 根据定义的列进行查找, 若是多列应满足"或"的逻辑
(eg: ["name__contains","email"] 姓名列里含有"dc"关键字或邮箱列里等于"dc"关键字的 前者模糊查找,后者精确查找!!

Q对象的使用, 简单举一个栗子:

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')
1
2
3
4
5
6
7
8
9

要点一: 搜索框是个表单, 用GET方式提交数据到后台!!
要点二: filter里多条件时,是依据and条件连接的; 该场景下我们应使用Q对象构造构造复杂的or逻辑!!
要点三: 控制关键字搜索表单是否进行显示!
要点四: 搜索关键字应该保留!

image-20240325140303819


# 批量操作

╮( ̄▽ ̄"")╭

先来看看效果

45

★ 注意4个要点!!

■ 要点1
  使用"自定义函数扩展", 在表格中添加checkbox列!  
■ 要点2
  在页面上 生成 批量操作的下拉框+按钮 !!  
  批量操作的下拉框select里有多少option用户通过action_list由自己自定制的; 
  action_list为空的话,页面上将不显示 批量操作的下拉框!  
  - 批注:Python处处皆对象,函数也是一个对象!往模版中传递一个函数的话,不用加括号,该函数会自动执行!
        So,我们将其转变成了字典的形式, {"函数对象.__name__ 即某个批量操作的函数名":该函数对象.text 即文本内容}
■ 要点3
  使用js将 散落各处的 批量操作的选择 和 表格里的多选框 放到 "同一个表单" 中, POST请求提交表单数据到后台!  
  - 批注:武sir是用form标签包裹起来的,因为这几个div是放在一起的;
        但我的代码,用了flex,这几处散落各地了,所以我用js变相实现啦!!
■ 要点4: 
  实现选中的批量操作(eg: 示例中是批量删除), 操作成功后, 可跳转其它页面!!
  - 批注:可能会用这样的应用场景,批量删除成功后,想让其跳转到详细的批量删除记录的页面!
    在示例代码中,批量操作的函数若无返回值,跳转的是当前页面;若有返回值,返回什么,就跳转什么!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

关键代码如下:

image-20240326142259916


# 组合搜索框

(*≧ω≦) 必拿下!

★ 一定一定一定 要明确整体的一个逻辑, 不然, 看代码会懵逼的(´・Д・)」!!
该小节的整体逻辑是这样子的, 如下:

search_group = [
    {"field":"gender", "db_condition":{}}
    {"field":"depart", "db_condition":{}}
]

"★" step1: 字典通过Option类进行了一次封装
search_group = [
    Option("gender"),
    Option("depart")
]

"★" step2: Option类通过SearchGroupRow类再进行了一次封装
相当于
search_group = [
    SearchGroupRow(gender字段对象的choices属性, self, title, request.GET)
    SearchGroupRow(depart外键字段对象所关联的那张表.objects.filter(**db_condition), self, title, request.GET),
]
SearchGroupRow实例化时的第一个参数就是 queryset_or_tuple, 它是 根据字段(choice、FK、M2M) 找到其关联的那些数据.
queryset_or_tuple可能是<QuerySet [<Depart: 司法部>,<Depart: 教育部>]>,也可能是((1, '男'), (2, '女'), (0, '未知'))

我们将上面的两个SearchGroupRow实例放到了search_group_row_list中,并传递给了前端模版
{% for row in search_group_row_list %}  # -- 每个row就是SearchGroupRow实例
    # -- ★这个for循环它会自动执行SearchGroupRow实例的__iter__方法
    #    __iter__方法中 会拿到 对应的queryset_or_tuple, 对其里面的每一个数据进行处理!!
    {% for item in row %}  
        {{ item|safe }}
    {% endfor %}
{% endfor %}
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

# 搜索条件封装和扩展

目标: 根据字段(choice、FK、M2M) 找到其关联的数据.
一代目有很多不完善的地方, 于是优化成了 二代目.. Hhh

大体思路:
1> 在自定义的handler中 (示例中是 UserInfoHandler), 配置search_group!
2> 根据字符串 (eg: "gender"、"depart") 去ORM表找到 对应的字段对象.
3> 先进行类型判断, 然后获取关联的数据!!

一代目的代码进行了以下的优化:
1. 不用字典,而是封装成了对象!对象的取值相较于字典取值代码更简洁 + 在search_group中配置时,也更简单.
   "★"要习惯这样的写法!!
2. 做了代码的拆分,将组合搜索的代码,拆分到了Option类中!
3. ★★★ 扩展点: 
   get_db_condition函数,我们可以写个类继承Option并重写该方法
   在重写的方法中,根据当前url来自定义一些复杂的搜索条件!!
   简单回顾下: path转换器--kwargs、无名分组--args、有名分组--kwargs、url参数--request.GET
1
2
3
4
5
6
7
8

get_db_condition(self,*args,**kwargs) 比如, 路由中的path转换器的值会被 **kwargs 接收!!

关键代码如下:

image-20240326195307435

截图中的示例中, 得到的组合搜索数据里面 要么是 queryset对象, 要么是元祖..
eg: <QuerySet [<Depart: 教育部1>, <Depart: 司法部>]> 、((1, '男'), (2, '女'), (0, '未知'))

# 搜索基本展示

在上面已经能拿到 组合搜索的数据, 现在需要展示在页面上, 供用户进行选择!!
目标: 在页面上显示组合搜索框, 框里有多个按钮选项!!

# 封装统一对象

将 queryset和元祖进行封装, 封装成一个统一的对象!!

分析如下:

现目前的代码, 得到的组合搜索数据里面 要么是 queryset对象, 要么是元祖 "你看上面的截图嘛,print的结果就是这样的!"
search_group_row_list = [<QuerySet [<Depart: 教育部1>, <Depart: 司法部>]>,((1, '男'), (2, '女'), (0, '未知'))]
紧接着拿到前端模版中展示,伪代码如下:
for row in search_group_row_list:
    for item in row:
        if item是对象:
            <a href="">item.title</a>
        else:
            <a href="">item.1</a>

在前端模版中,不好进行类型判断,最后展示的每个元素还要加a标签.后续还要实现默认选中等功能.
所以在伪代码的基础上还要加一系列的if-else判断.
这么分析下来,在前端模版中实现非常复杂,所以我们想着在后端尽可能的先实现这些,前端模版就只负责循环就好!!
1
2
3
4
5
6
7
8
9
10
11
12
13

ψ(`∇´)ψ 复习一个python的小知识!! 很重要哈!

# 若一个类中"定义了__iter__方法",且"该方法返回了一个迭代器",那么就称该类实例化对象是可迭代对象.
# - 即该对象可以被循环!当被循环时,会自动执行类中的__iter__方法!!
# - yield生成器是特殊的迭代器!

class SearchGroupRow:
    def __init__(self, queryset_or_tuple):
        self.queryset_or_tuple = queryset_or_tuple

    def __iter__(self):
        # return iter(self.queryset_or_tuple)
        yield 1
        yield 2
        yield 3


row = SearchGroupRow([1, 2, 3])
for item in row:
    print(item)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

具体关键代码实现如下:

image-20240327133655218

So, 我们成功将需要在前端模版中的判断放到了后端!! 棒!

# 文本显示自定制

像页面上展示的 "男"、"女" .. ORM表中就写死了, 是默认的.. 我们想让用户自定制呢?
扩展性更高啦!! 比如显示成 "男666"、"女666"..

在现目前已有的代码上进行了修改, 修改的关键代码在截图中已经做了标注!

image-20240327144258483

此时前端模版中的代码 {% for item in row %} 就是在执行 SearchGroupRow里的 __iter__ ..

# 组合搜索框的样式

(¯﹃¯)

关键代码如下:

image-20240327160313619


# 组合搜索实现

(*≧ω≦) 必拿下!

# 为按钮生成url

目标: 为组合搜索按钮生成url目标.

假设开发时配置的组合搜索是这样的:
search_group = [
    Option("gender"),
    Option("depart")
]
那么在Star组件中会对着两个Option实例进行进一步封装,进而得到两个SearchGroupRow实例!!
每个SearchGroupRow实例都有queryset_or_tuple属性.在该假设中,其属性值分别为
((1, "男"),(2, "女"),(0, "未知"),) 、<QuerySet [<Depart: 教育部>, <Depart: 司法部>]>

在页面上展示组合搜索框里有以下两行按钮:
性别    [全部] [男] [女] [未知]
部门    [全部] [教育部] [司法部]
1
2
3
4
5
6
7
8
9
10
11
12
# 组合搜索-单选

该示例的组合搜索是 性别的单选+部门的单选.

先来看看效果

46

接下来,我们来分析下, 为这些按钮添加url!! 遵循怎样的规则呢?
1> 生成的url不能影响当前访问的url! (即当我选中"男"后,再选中"司法部","男"的选中不受影响.
2> 生成的url中GET参数的值是按钮的文本在数据库中存储对应的值!
3> 第一次点击该按钮选中,再次点击同一按钮,应该取消选中!
4> 先选中"男",再选中"女", url的GET参数 depart的值 会由1变成了2.. 部门一行同理.
5> 当"男"、"女"、"未知" 都没选时,(即url中没有gender参数.) 性别一行的"全部"应该被选中! 部门一行同理.

■ step1: 我们访问http://127.0.0.1:8000/stark/app01/userinfo/list/时, 每个按钮渲染的url如下:
  [全部]    /stark/app01/userinfo/list/?          '选中' -- 当前访问的url中没有gender参数
  [男]      /stark/app01/userinfo/list/?gender=1  
  [女]      /stark/app01/userinfo/list/?gender=2  
  [未知]    /stark/app01/userinfo/list/?gender=0
  --- --- ---
  [全部]    /stark/app01/userinfo/list/?          '选中' -- 当前访问的url中没有depart参数
  [教育部]  /stark/app01/userinfo/list/?depart=1
  [司法部]  /stark/app01/userinfo/list/?depart=2
■ step2: 我们点击[男]这个按钮时,即访问地址 /stark/app01/userinfo/list/?gender=1, 每个按钮渲染的url如下:
  [全部]  /stark/app01/userinfo/list/?
  [男]    /stark/app01/userinfo/list/?            '选中' -- 将gender剔除掉,才能满足再次点击取消选中
  [女]    /stark/app01/userinfo/list/?gender=2  
  [未知]  /stark/app01/userinfo/list/?gender=0
  --- --- ---
  [全部]    /stark/app01/userinfo/list/?gender=1  '选中' -- 新渲染的url中有gender参数,是为了不影响gender的选中
  [教育部]  /stark/app01/userinfo/list/?gender=1&depart=1
  [司法部]  /stark/app01/userinfo/list/?gender=1&depart=2
■ step3: 我们再点击[女]这个按钮时,即访问地址 /stark/app01/userinfo/list/?gender=2, 每个按钮渲染的url如下:
  [全部]  /stark/app01/userinfo/list/?
  [男]    /stark/app01/userinfo/list/?gender=1
  [女]    /stark/app01/userinfo/list/?            '选中' -- 将gender剔除掉,才能满足再次点击取消选中
  [未知]  /stark/app01/userinfo/list/?gender=0
  --- --- ---
  [全部]    /stark/app01/userinfo/list/?gender=2          '选中'
  [教育部]  /stark/app01/userinfo/list/?gender=2&depart=1
  [司法部]  /stark/app01/userinfo/list/?gender=2&depart=2
■ step3: 我们再点击[司法部]这个按钮时,即访问地址 /stark/app01/userinfo/list/?gender=2&depart=2, 每个按钮渲染的url如下:
  [全部]  /stark/app01/userinfo/list/?depart=2
  [男]    /stark/app01/userinfo/list/?gender=1&depart=2
  [女]    /stark/app01/userinfo/list/?depart=2            '选中'--将gender剔除掉,再次点击取消选中且不影响depart的选中
  [未知]  /stark/app01/userinfo/list/?gender=0&depart=2
  --- --- ---
  [全部]    /stark/app01/userinfo/list/?gender=2
  [教育部]  /stark/app01/userinfo/list/?gender=2&depart=1
  [司法部]  /stark/app01/userinfo/list/?gender=2           '选中'--将depart剔除掉,再次点击取消选中且不影响gender的选中
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

关键代码如下: (让我自己写,够呛(¯﹃¯)

image-20240327225941699

# 组合搜索-多选

在配置search_group时, 若给Option实例化多添加一个参数 is_multi=True, 那么该字段的组合搜索就支持多选!!
默认是不支持的.

eg: search_group = [Option("gender"),Option("depart", is_multi=True)]

先来看看效果:

48

关键代码如下: (规则看代码注释, 单选看懂了, 多选也就那么回事...

image-20240328140249517

# 进行筛选

★ 你看代码! 关键字搜索和组合搜索是一起生效的!!

先来看看效果:

47

关键代码如下: (规则啥的详见注释, 我累了(・_・;

image-20240328134717587


# ※ 后续: 数据过滤

列表显示的数据先进行下过滤!! / 列表数据显示前作额外的约束. ★ 这也是个扩展点!!
★ 编辑页面和删除页面针对的obj, 需要啥过滤条件得到.. 也作了相应的扩展! 在下小节的CURD的页面自定制中进行了蓝色标注!!

image-20240403155748710


# ※ 后续: CURD的页面自定制

CURD的页面支持自定制, 不指定的话就用默认的

关键代码如下, (红色标注的部分

image-20240408135635845

截图中的代码,有一点纰漏,新增和添加和删除页面的GET请求使用的模版应与post请求一样,需要这样写!
self.add_template or 'stark/change.html'
self.change_template or 'stark/change.html'
self.delete_template or 'stark/delete.html'
1
2
3
4

# ※ 后续: 反向生成url的优化!

(*≧ω≦)

说到底, 考察的就是实参和形参的传递!

image-20240408183830632


# ◎ BUG

解决一个bug!!

加distinct()去重!! 场景: 多对多字段, 在组合搜索里 多选时!!

queryset = prev_queryset.filter(conn).filter(**search_group_condition).distinct().order_by(*order_list)
# 若不加distinct,会在表格里显示重复行!
1
2

# 附录

该篇博客实现的stark组件的功能, 都在下方展示啦! (相当于说明文档了 (*≧ω≦)

示例代码如下:

from apps.stark.service.v1 import site, StarkHandler, get_choice_text, StarkModelForm, Option

class MyOption(Option):
    def get_db_condition(self, request, *args, **kwargs):
        # return {'id__gt': request.GET.get('nid')}
        return {'id__gt': 0}


class UserInfoHandler(StarkHandler):
    list_display = [StarkHandler.display_checkbox,
                    "name", "age", "email", "depart",
                    get_choice_text("性别", 'gender'),
                    StarkHandler.display_edit,
                    StarkHandler.display_del]

    order_list = ["-id"]  # 表格中数据的排序规则
    search_list = ["name__contains", "email__contains"]  # 关键字搜索

    # 里面有多少元素,批量操作的select下拉框里就有多少个option
    action_list = [StarkHandler.action_multi_delete, ]

    search_group = [
        Option("gender"),
        Option("depart", {'id__gt': 0}, is_multi=True),
        # MyOption("depart", is_multi=True),  # 自定义复杂的查询规则
        # Option("gender", text_func=lambda item: item[1] + "666"),  # 显示的文本自定制
    ]

site.register(models.UserInfo, UserInfoHandler)
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

目前, stark组件有700多行代码, 篇幅原因就不粘贴啦!!


CURD
基础业务处理

← CURD 基础业务处理→

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