zoukankan      html  css  js  c++  java
  • stark

    Ⅰ 排序

    当数据量增多,对于数据 我们应该能够指定如何排序的。且此功能应该是可以给用户自定义进行配置的。

    这是StarkHandler类的方法
    1
    order_list = [] 2 def get_order_list(self): 3 return self.order_list or ['id'] #默认是按照id排序

     Combine▼

    list_view下的排序功能:
    1
    order_list = self.get_order_list() 2 3 #组合搜索条件字典 4 search_dict = self.get_search_group_condition(request) 5 6 #自定义方法筛选后的结果 7 queryset = self.get_queryset(**kwargs) 8 9 queryset = queryset.filter(conn).filter(**search_dict).all().order_by(*order_list)

    用户使用配置只需在子类Handler写上

     1 order_list = ['-id','name']    #优先根据-id排序,完后再根据name排序        

    Ⅱ 模糊搜索

    实现思路:

    在页面设置form表单,搜索:以Get形式提交到后台。后台获取数据然后进行筛选过滤。

    后端获取关键字之后,根据定义的列进行查找(多列可以按照 "或" 进行查询)

    同样,预留勾子,功能可以给用户自定义进行配置的。

    这是StarkHandler类的方法
    1
    search_list = [] #为空的话,模板默认不显示搜索按钮 2 def get_search_list(self): 3 return self.search_list

    Combine▼

     list_view下的模糊搜索功能: 
    1
    search_list = self.get_search_list() 2 search_value = request.GET.get('q','') 3 4 from django.db.models import Q #实现“或”查询 5 conn = Q() 6 conn.connector = 'OR' 7 8 if search_value: 9 #Q,用于构造复杂的ORM查询条件 10 for item in search_list: 11 conn.children.append((item,search_value))

    即为上面的 queryset = queryset.filter(conn).filter(**search_dict).all().order_by(*order_list)

    用户使用配置只需在子类Handler写上

    1 search_list = ['name'] 
    2 search_list = ['name__contains','email__contains']

    Ⅲ 批量操作

    首先,添加checkbox列

     1     def display_checkbox(self,obj=None,is_header=None):
     2         """
     3         生成checkbox 批量操作
     4         :param obj:
     5         :param is_header:
     6         :return:
     7         """
     8         if is_header:
     9             return "选择"
    10         return mark_safe('<input type="checkbox" name="pk" value="%s">'%obj.pk)
    #name属性对表单数据进行标识,通过request.POST.getlist('pk')获得选取值。

       如图  →    


    然后,生成批量操作按钮

    考虑到以后功能拓展,功能数量增加,生成过多按钮而影响页面的布局,

    为此,这里以下拉框的形式展示这些功能。即<select><option>Function</option></select>

       如图  →    

    同样,预留勾子,功能可以给用户自定义进行配置

    这是StarkHandler类的方法
    1
    action_list = [] #默认是没有批量操作的 2 def get_action_list(self): 3 """ 4 处理批量操作等自定义方法 5 :return: 6 """ 7 return self.action_list

    下面是"一键删除"的例子

     这是StarkHandler类的方法
    1
    def multi_delete(self,request): 2 """ 3 批量删除 4 :param request: 5 :return: 6 """ 7 pk_list = request.POST.getlist('pk') 8 self.model_class.objects.filter(id__in=pk_list).delete() 9 10 multi_delete.text = '一键删除' #在python中,一切皆对象,都可以使用反射。

    用户使用配置只需在子类Handler写上

    1 action_list = [StarkHandler.multi_delete,] #用户可以在子类自定义方法,添加到action_list
    

     展示的形式 与 可拓展 解决了,下一步我们只要处理请求传入过来的数据即可。

    处理action_list & 获取操作并执行

    action_list不能直接放在模板进行循环展示,因为模板中 {{ func }}会自动执行

    因此我们得把获得action_list做小小的处理,做成字典的形式{ 函数名 : 函数文本名称 },再到模板中显示。

    list_view下处理action_list:
    1
    action_list = self.action_list 2 action_dict = {func.__name__:func.text for func in action_list} 3 4 if request.method == 'POST': 5 func_name = request.POST.get('action') 6 if func_name and func_name in action_dict: #逻辑优化,防止被 方法乱入 7 func = getattr(self,func_name) 8 func(request)

    Ⅳ 组合搜索

    根据字段找到关联的数据choice、ForeignKey、ManyToMany 

    实现思路:

    第一步:配置

    预留勾子,功能可以给用户自定义进行配置

    这是StarkHandler类的方法
    1
    search_group = [] 2 def get_search_group(self): 3 return self.search_group

    第二部:根据配置获取数据。根据用户写入的字符串,去自己对应的Model类中找到 字段对象。

    但是怎么知道传入进来的字符串对应的字段对象是 choice字段、FK还是M2M?

    这里我们可以通过类型进行判断一下 ▼

    1 from django.db.models import ManyToManyField,ForeignKey
    2 
    3 field_object = model_class._meta.get_field(self.field) 
    4 #choice_obj: app01.UserInfo.gender 
    5 #FK_oobj: app01.UserInfo.depart
    6 if isinstance(field_object,ForeignKey) or isinstance(field_object,ManyToManyField):
    7  #Fk和M2M,应该去获取其相关表中的数据
    8 print(field_object.remote_field.model) 获得关联表的类
    9 else:
    10  #获取choice中的数据 
    11 print(field_object.choices)

    第三部:根据配置获取数据(含条件)。在实现 组合搜索按钮 查找数据的时候,可以把关联的数据全部获取到,但是我们的代码应该可以支持设置条件的,也就是可以添加筛选条件等。比如想要生成一个age>5的搜索按钮。且可以给用户自定义设置的。

    不要用字典列表反复嵌套。纵观写得牛逼的源码,封装的时候很少使用dict,tuple,一般都是封装成类,再去类中获取。

    为了把上面想到的功能优雅地写入serch_group,我们进行简单的代码拆分,封装到一个类中。

     1 class Option(object):
     2     def __init__(self,field,is_multi=False,db_condition=None,text_func=None,value_func=None):
     3         """
     4         :param field: 组合搜索关联的字段
     5         :param is_multi: 是否支持多选
     6         :param db_condition: 数据库关联查询时的条件
     7         :param text_func: 此函数用于组合搜索按钮 展示 文本或者图案
     8         :param value_func: 此函数用于显示组合搜索按钮值
     9         """
    10         self.field = field
    11         self.is_multi = is_multi
    12         if not db_condition:
    13             db_condition = {} #默认是为空的,即无查询条件
    14         self.db_condition = db_condition
    15         self.text_func = text_func
    16         self.value_func = value_func
    17 
    18         self.is_choice = False
    19 
    20     def get_db_condition(self):
    21         return self.db_condition
    22 
    23     def get_queryset_or_tuple(self,model_class,request,*args,**kwargs): #第二步的处理封装在这里
    24         """
    25 
    26         :param model_class:
    27         :param request:
    28         :param args:
    29         :param kwargs:
    30         :return:
    31         """
    32         field_object = model_class._meta.get_field(self.field) #choice:app01.UserInfo.gender #FK:app01.UserInfo.depart
    33 
    34         title = field_object.verbose_name
    35         if isinstance(field_object,ForeignKey) or isinstance(field_object,ManyToManyField):
    36             db_condition = self.get_db_condition()
    37             #### 返回的是QuerySet类型 ####
    38             #print(field_object.remote_field.model.objects.filter(**db_condition)) <QuerySet [<Depart: 技术部>, <Depart: 美术部>]>
    39             return SearchGroupRow(title,field_object.remote_field.model.objects.filter(**db_condition),self,request.GET) #self是Option类对象
    40         else:
    41             #### 获取choice中的数据 / 返回的是元祖类型 ####
    42             self.is_choice = True
    43             # print(field_object.choices)  ((1, '男'), (2, '女'))
    44             return SearchGroupRow(title,field_object.choices,self,request.GET)
    45 
    46     def get_text(self,field_object):
    47         if self.text_func:  #如果自定义了函数,则用自己的
    48             return self.text_func(field_object)
    49 
    50         if self.is_choice:
    51             return field_object[1]
    52 
    53         return str(field_object)
    54 
    55     def get_value(self,field_object):
    56         if self.value_func:  #自定制
    57             return self.value_func(field_object)
    58 
    59         if self.is_choice:
    60             return field_object[0]
    61 
    62         return field_object.pk

    用户使用配置只需在子类Handler写上,

    且用户如果需要根据自己的情况处理db_condition,继承该类,重写方法,返回字典。

    1   search_group = [Option('gender',is_multi=True),]
    2 search_group = [Option('name',db_condition={"id__gt":5})]

    第四步:在页面上显示组合搜索的按钮内容(将QuerySet tuple进行封装)

    上面,我们针对choice,FK,M2M 字段对象区分处理了,但是FK,M2M返回的的是QuerySet类型,而choice返回的是元祖。

    如果把结果直接拿到模板渲染的话,模板上还需要 if...else 再次判断,区分处理,循环数据,前端就会莫名复杂。

    想法:

    把代码放到后端,让后端通过python进行判断,而模板只进行循环展示。

    这里我们重新封装一个类,把前面得到的结果传入进行处理,返回统一的对象

    补充一个知识点

     1 # 一般情况下实例化的对象是不可以被迭代的
     2 # 如果一个类中定义了__iter__方法且该方法返回一个迭代器,那么就称该实例化的对象为可迭代对象。即对象可以被循环。
     3 
     4 class test():
     5     def __init__(self,name):
     6         self.name = name
     7     def __iter__(self):
     8         return iter([1,2,3,4])
     9 
    10 # 生成器是一种特殊的迭代器。所以还可以这么写,
    11     def __iter__(self):
    12         yield 1
    13         yield 2
    14         yield 3
    View Code

    在此基础下,根据传入不同结果进行判断,再返回相应可迭代对象。

    class SearchGroupRow(object):
        def __init__(self,title,queryset_or_tuple, option_object,query_dict):
            """
    
            :param title: 组合搜索的列名称
            :param queryset_or_tuple: 组合搜索关联获取到的数据
            :param option_object: Option对象
            :param query_dict: request.GET
            """
            self.title = title
            self.queryset_or_tuple = queryset_or_tuple
            self.option_object = option_object
            self.query_dict = query_dict
    
        def __iter__(self):
            yield mark_safe('<span class="search_group label label-default">%s</span>'%self.title)
    
            for item in self.queryset_or_tuple:
                text = self.option_object.get_text(item) #性别 /部门
                value = self.option_object.get_value(item) #'1' /技术部
    
                query_dict = self.query_dict.copy() #{'gender': ['1']}  #生成下面URL需要(1)
                query_dict._mutable = True
    
                if not self.option_object.is_multi:
                    request_value_list = query_dict.getlist(self.option_object.field) #获取GET数据 ['2']
                    query_dict[self.option_object.field] = value #生成下面URL需要(2)
                    # print(text,str(value),request_value_list)
                    if str(value) in request_value_list:
                        #通过去除 ?gender=2 重新赋值URL,实现再选取时 自动消失样式
                        query_dict.pop(self.option_object.field)
                        yield mark_safe('<a class="btn btn-danger active" href="?%s">%s</a>'%(query_dict.urlencode(),text))
                    else:
                        yield mark_safe('<a class="btn btn-default" href="?%s">%s</a>'%(query_dict.urlencode(),text))
    
                else:
                    multi_request_value_list =query_dict.getlist(self.option_object.field) #['1','2']
                    if str(value) in multi_request_value_list:
                        multi_request_value_list.remove(str(value))
                        query_dict.setlist(self.option_object.field,multi_request_value_list)
                        yield mark_safe('<a class="btn btn-danger active" href="?%s">%s</a>'%(query_dict.urlencode(),text))
                    else:
                        multi_request_value_list.append(str(value))
                        query_dict.setlist(self.option_object.field,multi_request_value_list)
                        yield mark_safe('<a class="btn btn-default" href="?%s">%s</a>'%(query_dict.urlencode(),text))

    Combine above▼

    1 #list_view下处理search_group:
    2         search_group_row_list = []
    3         search_group = self.get_search_group() # ['gender', 'depart']
    4         for keyword_object in search_group:  #keyword是一个Option类实例对象
    5             row = keyword_object.get_queryset_or_tuple(self.model_class,request,*args,**kwargs)
    6             #返回的是一个SearchGroupRow对象,且可迭代
    7             search_group_row_list.append(row)

    模板传入seach_group_row_list,循环字段对象、再渲染 → 如图:

    第五步:为组合搜索按钮生成URL

    多组搜索按钮,生成URL时,应该不影响其他组的条件。即 /?gender=1&depart=1。

    1.根据 Option.get_text ,Option.get_value 返回字段对象 text值和所有value值

    2.生成带参数的URL,渲染模板

    具体代码紫色部分

    第六步:获取请求的数据进行筛选

    获取 多组搜索按钮URL参数 后,需要根据条件筛选数据。

    在筛选数据之前,如果我想实现支持单选和多选的多组搜索按钮?进而获取呢?

     1     def get_search_group_condition(self,request):
     2         condition = {}
     3         for option_object in self.get_search_group():
     4             if option_object.is_multi:   5                 value_list = request.GET.getlist(option_object.field) #tags=[1,2]
     6                 if not value_list:
     7                     continue
     8                 condition['%s__in' %option_object.field] = value_list
     9             else:
    10                 value = request.GET.get(option_object.field)
    11                 if not value:
    12                     continue
    13                 condition[option_object.field] = value
    14 
    15         return condition

    返回字典(筛选条件单选或多选),交给list_view处理。

    1 queryset = queryset.filter(conn).filter(**search_dict).all().order_by(*order_list)
    

    暂时想到这么多....

  • 相关阅读:
    ELK 安装与配置
    redis参数与持久化原理
    python_way ,day26 django_admin 自定义
    python_way ,day25 wmi
    docker系列详解<一>之docker安装
    SpringCloud学习系列<一>版本介绍
    docker 安装MongoDB以及设置用户
    Caused by: java.lang.IllegalArgumentException
    tomcat启动时间5分钟左右org.apache.catalina.util.SessionIdGeneratorBase.createSecureRandom Creation of SecureRandom instance for session ID generation using [SHA1PRNG] took [342,445] milliseconds.
    Spring 全局异常拦截根据业务返回不同格式数据 自定义异常
  • 原文地址:https://www.cnblogs.com/steven2020/p/10714211.html
Copyright © 2011-2022 走看看