关于项目中组合搜索的一点代码

2018-03-28  本文已影响0人  不_一

CRM组合搜索实现关键代码

Config配置相关,创建CombineFilterOption对象组成列表

class UserInfoConfig(v1.StarkConfig):
    list_display = ['name', 'username', 'email', 'depart']
    edit_link = ['name']
    # model_form_class = UserInfoModelForm
    show_search_form = True
    show_combine_filter = True
    combine_filter = [
        v1.CombineFilterOption('depart', text_func_name=lambda x: str(x), val_func_name=lambda x: x.code),
    ]


v1.site.register(models.UserInfo, UserInfoConfig)

组合搜索option类的定义

class CombineFilterOption(object):
    """
    用于对组合筛选条件配置数据的封装(抽象)
    [('字段名称', 是否多选, 字段的筛选条件, 是否是choice),
     ('字段名称', 是否多选, 字段的筛选条件, 是否是choice),
     ('字段名称', 是否多选, 字段的筛选条件, 是否是choice),]
    """

    def __init__(self, field_name, multi=False, condition=None, is_choice=False, text_func_name=None,
                 val_func_name=None):
        """
        :param field_name: 组合筛选字段
        :param multi: 某个字段筛选是否是多选
        :param condition: 某个字段是有多个可选项,但不是所有项都支持筛选
        :param is_choice: 某个字段是不是choice类型,一般有三个类型(ForeignKey,ManyToManyField,choice)
        :param text_func_name: 组合筛选时,针对Foreignkey或字段M2M,筛选条件的值是什么,默认是pk(id)
        :param val_func_name: 组合筛选时,针对foreignkey或M2M 显示文本是什么?默认是str(obj),可以显示为id或者关联字段
        """
        self.field_name = field_name
        self.multi = multi
        self.condition = condition
        self.is_choice = is_choice
        self.text_func_name = text_func_name
        self.val_func_name = val_func_name

    def get_queryset_display(self, field_obj):
        """
        :param field_obj: models.CharField(max_length=32) 对象
        :return: 根据field_obj 反查该字段的类,即数据表,然后根据表去查数据
        """
        if self.condition:
            return field_obj.rel.to.objects.filter(**self.condition)
        return field_obj.rel.to.objects.all()

    def get_choice_display(self, field_obj):
        """
        获取choice类型的筛选条件的显示值
        :param field_obj:
        :return:
        """
        return field_obj.choices

前端渲染:

{% if cl.show_combine_filter %}
        <div class="combine_filter" style="margin-top:20px;">
            {% for item in cl.get_comb_filter_data %}
                <div class="combine_filter_line">
                    {% for col_html in item %}
                        {{ col_html }}
                    {% endfor %}
                </div>
            {% endfor %}
        </div>
    {% endif %}

ChangeList类的方法get_comb_filter_data

通过for循环拿到一个个实际生成前端a标签的CombineFilterRow对象

 def get_comb_filter_data(self):
        """
        生成器函数,根据组合筛选的配置,从数据库中拿到前端页面要显示的数据,
        页面显示的一行行数据可以说是一种数据类型,可以对该数据进行抽象,定义一个类
        cfo_obj.get_queryset_display(field_obj)---CombineFilterOption 对象的一个方法,根据该方法可以拿到models中某类的数据
        该方法需要一个field_obj参数,其为model class类中的一个字段(title = models.CharField(max_length)
        row是一个对象,可迭代对象.
        :return:
        """
        from django.db.models import ForeignKey, ManyToManyField
        for cfo_obj in self.get_combine_filter:
            field_obj = self.model_class._meta.get_field(cfo_obj.field_name)
            if isinstance(field_obj, ForeignKey):
                row = CombineFilterRow(cfo_obj, cfo_obj.get_queryset_display(field_obj), self.config.request)
            elif isinstance(field_obj, ManyToManyField):
                row = CombineFilterRow(cfo_obj, cfo_obj.get_queryset_display(field_obj), self.config.request)
            else:
                row = CombineFilterRow(cfo_obj, cfo_obj.get_choice_display(field_obj), self.config.request)
            yield row

从上面代码来看,for循环组合搜索列表,然后yield拿到一个CombineFilterRow对象,这个对象定义了iter方法用来生成一个个a标签
代码定义如下

class CombineFilterRow(object):
    """
    联合筛选要显示的数据对象,该对象不是简单的字典或者列表,而是一个类对象
    一个对象代表一个筛选条件,一个对象拥有一个__iter__方法,该方法用于拿到一个个筛选值
    """

    def __init__(self, option, data, request):
        """
        :param option: 配置项 v1.CombineFilterOption('gender', is_choice=True) 一个对象
        :param data: 通过配置从数据库中选取的页面要显示的数据:<QuerySet [<Department: 灯光>,
                                                                    <Department: 动作指导>]>
        :param request: 当前页面request请求
        """
        self.data = data
        self.option = option
        self.request = request

    def __iter__(self):
        """
        该方法是针对一个对象:当for循环一个对象时,调用__iter__方法
        :return:前端对CombineFilterRow对象进行循环时,生成一系列的a标签
        """
        params = self.request.GET.copy()  # 拿到当前页面的筛选条件
        url_key = self.option.field_name  # 调用该方法当前字段名称,该名称为url中的筛选条件.生成url用
        current_val = params.get(url_key)  # 单选用,当前值可能为空
        current_val_list = params.getlist(url_key)  # 多选用,值也可能为空
        current_path_info = self.request.path_info

        if url_key in params:  # 为全选a标签做判断
            params_copy = params.copy()
            params_copy.pop(url_key)
            select_all_url = "{0}?{1}".format(current_path_info, params_copy.urlencode())
            yield mark_safe('<a href="{0}">全部</a>'.format(select_all_url))
        else:  # url_key没在params,说明对该字段没有做筛选,即全选选中
            select_all_url = "{0}?{1}".format(current_path_info, params.urlencode())
            yield mark_safe('<a href="{0}" class="active">全部</a>'.format(select_all_url))

        for data_obj in self.data:  # 为当前字段的可选项,进行a标签生成
            if self.option.is_choice:
                pk, text = str(data_obj[0]), data_obj[1]
            else:
                text = self.option.text_func_name(data_obj) if self.option.text_func_name else str(data_obj)
                pk = self.option.val_func_name(data_obj) if self.option.val_func_name else data_obj.pk
                # pk, text = str(data_obj.pk), str(data_obj)
            if self.option.multi:  # 根据单选还是多选给每个a标签赋不同的href值
                # 多选
                params_copy = params.copy()
                val_list = params_copy.getlist(url_key)
                if pk in current_val_list:
                    val_list.remove(pk)
                    params_copy.setlist(url_key, val_list)
                    multi_select_url = "{0}?{1}".format(current_path_info, params_copy.urlencode())
                    yield mark_safe('<a href="{1}" class="active">{0}</a>'.format(text, multi_select_url))
                else:
                    val_list.append(pk)
                    params_copy.setlist(url_key, val_list)
                    multi_select_url = "{0}?{1}".format(current_path_info, params_copy.urlencode())
                    yield mark_safe('<a href="{1}">{0}</a>'.format(text, multi_select_url))
            else:
                params[url_key] = pk  # 为这一行每个点击的a标签,获取href值用
                single_select_url = "{0}?{1}".format(current_path_info, params.urlencode())
                if current_val == pk:
                    yield mark_safe('<a href="{1}" class="active">{0}</a>'.format(text, single_select_url))
                else:
                    yield mark_safe('<a href="{1}" >{0}</a>'.format(text, single_select_url))

        if self.option.multi:
            yield mark_safe('<span class="text-danger pull-right center-block" style="margin-right: 100px;">多选</span>')
        else:
            yield mark_safe('<span class="text-danger pull-right center-block" style="margin-right: 100px;">单选</span>')
上一篇 下一篇

猜你喜欢

热点阅读