7

组合搜索组件文档 - huxiaofeng

 1 year ago
source link: https://www.cnblogs.com/Maxs-message/p/17320336.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

组合搜索组件

1. 先展示一下使用效果:

2709341-20230415001107341-1130954175.png

2. 使用方法

  • 第一步: 在views.py中配置和传参
search_group = NbSearchGroup(
    request,
    models.TransactionRecord,  # 传入表
    Option('charge_type'),  # 传入choice字段名
)
2709341-20230415001135680-1587487253.png
  • 第二步: 添加查询条件
# 查询条件 .filter(**search_group.get_condition)
queryset = models.TransactionRecord.objects.filter(q).filter(**search_group.get_condition).filter(
    customer_id=request.user_obj.id, active=1).order_by(
    '-id')
  • 第三步: 向html中传递参数
context = {
    'show_data': pager.show_data,
    'pager_string': pager.html(),
    'keyword': keyword,
    "search_group": search_group  # 这是需要添加的
}
return render(request, 'transaction_list.html', context)
2709341-20230415001203453-2135663808.png
  • 第四步: 导入html search_group.html
{% if search_group.get_row_list %}
    <div class="panel panel-default">
        <div class="panel-heading">
            <i class="fa fa-filter" aria-hidden="true"></i> 快速筛选
        </div>
        <div class="panel-body">
            <div class="search-group">
                {% for row in search_group.get_row_list %}
                    <div class="row">
                        {% for obj in row %}
                            {{ obj|safe }}
                        {% endfor %}
                    </div>
                {% endfor %}
            </div>
        </div>
    </div>
{% endif %}
  • 第五步: 导入css(最好放到static静态文件中) search_group.css
.search-group {
    padding: 5px 10px;
}

.search-group .row .whole {
    width: 60px;
    float: left;
    display: inline-block;
    padding: 5px 0 5px 8px;
    margin: 3px;
    font-weight: bold;
    text-align: right;

}

.search-group .row .others {
    padding-left: 80px;
}

.search-group .row a {
    display: inline-block;
    padding: 5px 8px;
    margin: 3px;
    border: 1px solid #d4d4d4;

}

.search-group .row a {
    display: inline-block;
    padding: 5px 8px;
    margin: 3px;
    border: 1px solid #d4d4d4;
}

.search-group a.active {
    color: #fff;
    background-color: #337ab7;
    border-color: #2e6da4;
}
  • 第六步: 导入核心代码(源码)group.py
# -*- encoding:utf-8 -*-
# @time: 2023/4/14 21:03
# @author: Maxs_hu
from django.db.models import ForeignKey, ManyToManyField


class SearchGroupRow(object):
    def __init__(self, title, queryset_or_tuple, option, query_dict):
        """
        :param title: 组合搜索的列名称
        :param queryset_or_tuple: 组合搜索关联获取到的数据
        :param option: 配置
        :param query_dict: request.GET
        """
        self.title = title
        self.queryset_or_tuple = queryset_or_tuple
        self.option = option
        self.query_dict = query_dict

    def __iter__(self):
        yield '<div class="whole">'
        yield self.title
        yield '</div>'
        yield '<div class="others">'
        total_query_dict = self.query_dict.copy()
        total_query_dict._mutable = True

        origin_value_list = self.query_dict.getlist(self.option.field)
        if not origin_value_list:
            yield "<a class='active' href='?%s'>全部</a>" % total_query_dict.urlencode()
        else:
            total_query_dict.pop(self.option.field)
            yield "<a href='?%s'>全部</a>" % total_query_dict.urlencode()

        for item in self.queryset_or_tuple:
            text = self.option.get_text(item)
            value = str(self.option.get_value(item))
            query_dict = self.query_dict.copy()
            query_dict._mutable = True

            if not self.option.is_multi:
                query_dict[self.option.field] = value
                if value in origin_value_list:
                    query_dict.pop(self.option.field)
                    yield "<a class='active' href='?%s'>%s</a>" % (query_dict.urlencode(), text)
                else:
                    yield "<a href='?%s'>%s</a>" % (query_dict.urlencode(), text)
            else:
                # {'gender':['1','2']}
                multi_value_list = query_dict.getlist(self.option.field)
                if value in multi_value_list:
                    multi_value_list.remove(value)
                    query_dict.setlist(self.option.field, multi_value_list)
                    yield "<a class='active' href='?%s'>%s</a>" % (query_dict.urlencode(), text)
                else:
                    multi_value_list.append(value)
                    query_dict.setlist(self.option.field, multi_value_list)
                    yield "<a href='?%s'>%s</a>" % (query_dict.urlencode(), text)

        yield '</div>'


class Option(object):
    def __init__(self, field, is_condition=True, is_multi=False, db_condition=None, text_func=None, value_func=None):
        """
        :param field: 组合搜索关联的字段
        :param is_multi: 是否支持多选
        :param db_condition: 数据库关联查询时的条件
        :param text_func: 此函数用于显示组合搜索按钮页面文本
        :param value_func: 此函数用于显示组合搜索按钮值
        """
        self.field = field
        self.is_condition = is_condition
        self.is_multi = is_multi
        if not db_condition:
            db_condition = {}
        self.db_condition = db_condition
        self.text_func = text_func
        self.value_func = value_func

        self.is_choice = False

    def get_db_condition(self, request, *args, **kwargs):
        return self.db_condition

    def get_queryset_or_tuple(self, model_class, request, *args, **kwargs):
        """
        根据字段去获取数据库关联的数据
        :return:
        """
        # 根据gender或depart字符串,去自己对应的Model类中找到 字段对象
        field_object = model_class._meta.get_field(self.field)
        title = field_object.verbose_name
        # 获取关联数据
        if isinstance(field_object, ForeignKey) or isinstance(field_object, ManyToManyField):
            # FK和M2M,应该去获取其关联表中的数据: QuerySet
            db_condition = self.get_db_condition(request, *args, **kwargs)
            return SearchGroupRow(title,
                                  field_object.remote_field.model.objects.filter(**db_condition),
                                  self,
                                  request.GET)
        else:
            # 获取choice中的数据:元组
            self.is_choice = True
            return SearchGroupRow(title, field_object.choices, self, request.GET)

    def get_text(self, field_object):
        """
        获取文本函数
        :param field_object:
        :return:
        """
        if self.text_func:
            return self.text_func(field_object)

        if self.is_choice:
            return field_object[1]

        return str(field_object)

    def get_value(self, field_object):
        if self.value_func:
            return self.value_func(field_object)

        if self.is_choice:
            return field_object[0]

        return field_object.pk

    def get_search_condition(self, request):
        if not self.is_condition:
            return None
        if self.is_multi:
            values_list = request.GET.getlist(self.field)  # tags=[1,2]
            if not values_list:
                return None
            return '%s__in' % self.field, values_list
        else:
            value = request.GET.get(self.field)  # tags=[1,2]
            if not value:
                return None
            return self.field, value


class NbSearchGroup(object):
    def __init__(self, request, model_class, *options):
        self.request = request
        self.model_class = model_class
        self.options = options

    def get_row_list(self):
        row_list = []
        for option_object in self.options:
            row = option_object.get_queryset_or_tuple(self.model_class, self.request)
            row_list.append(row)
        return row_list

    @property
    def get_condition(self):
        """
        获取组合搜索的条件
        :param request:
        :return:
        """
        condition = {}
        # ?depart=1&gender=2&page=123&q=999
        for option in self.options:
            key_and_value = option.get_search_condition(self.request)
            if not key_and_value:
                continue
            key, value = key_and_value
            condition[key] = value

        return condition

3. 分享源码实现和封装

from django.db.models import ForeignKey, ManyToManyField


class SearchGroupRow(object):
    def __init__(self, title, queryset_or_tuple, option, query_dict):
        """
        :param title: 组合搜索的列名称
        :param queryset_or_tuple: 组合搜索关联获取到的数据
        :param option: 配置
        :param query_dict: request.GET
        """
        self.title = title
        self.queryset_or_tuple = queryset_or_tuple
        self.option = option
        self.query_dict = query_dict

    def __iter__(self):
        yield '<div class="whole">'
        yield self.title
        yield '</div>'
        yield '<div class="others">'
        total_query_dict = self.query_dict.copy()
        total_query_dict._mutable = True

        origin_value_list = self.query_dict.getlist(self.option.field)
        if not origin_value_list:
            yield "<a class='active' href='?%s'>全部</a>" % total_query_dict.urlencode()
        else:
            total_query_dict.pop(self.option.field)
            yield "<a href='?%s'>全部</a>" % total_query_dict.urlencode()

        for item in self.queryset_or_tuple:
            text = self.option.get_text(item)
            value = str(self.option.get_value(item))
            query_dict = self.query_dict.copy()
            query_dict._mutable = True

            if not self.option.is_multi:
                query_dict[self.option.field] = value
                if value in origin_value_list:
                    query_dict.pop(self.option.field)
                    yield "<a class='active' href='?%s'>%s</a>" % (query_dict.urlencode(), text)
                else:
                    yield "<a href='?%s'>%s</a>" % (query_dict.urlencode(), text)
            else:
                # {'gender':['1','2']}
                multi_value_list = query_dict.getlist(self.option.field)
                if value in multi_value_list:
                    multi_value_list.remove(value)
                    query_dict.setlist(self.option.field, multi_value_list)
                    yield "<a class='active' href='?%s'>%s</a>" % (query_dict.urlencode(), text)
                else:
                    multi_value_list.append(value)
                    query_dict.setlist(self.option.field, multi_value_list)
                    yield "<a href='?%s'>%s</a>" % (query_dict.urlencode(), text)

        yield '</div>'


class Option(object):
    def __init__(self, field, is_condition=True, is_multi=False, db_condition=None, text_func=None, value_func=None):
        """
        :param field: 组合搜索关联的字段
        :param is_multi: 是否支持多选
        :param db_condition: 数据库关联查询时的条件
        :param text_func: 此函数用于显示组合搜索按钮页面文本
        :param value_func: 此函数用于显示组合搜索按钮值
        """
        self.field = field
        self.is_condition = is_condition
        self.is_multi = is_multi
        if not db_condition:
            db_condition = {}
        self.db_condition = db_condition
        self.text_func = text_func
        self.value_func = value_func

        self.is_choice = False

    def get_db_condition(self, request, *args, **kwargs):
        return self.db_condition

    def get_queryset_or_tuple(self, model_class, request, *args, **kwargs):
        """
        根据字段去获取数据库关联的数据
        :return:
        """
        # 根据gender或depart字符串,去自己对应的Model类中找到 字段对象
        field_object = model_class._meta.get_field(self.field)
        title = field_object.verbose_name
        # 获取关联数据
        if isinstance(field_object, ForeignKey) or isinstance(field_object, ManyToManyField):
            # FK和M2M,应该去获取其关联表中的数据: QuerySet
            db_condition = self.get_db_condition(request, *args, **kwargs)
            return SearchGroupRow(title,
                                  field_object.remote_field.model.objects.filter(**db_condition),
                                  self,
                                  request.GET)
        else:
            # 获取choice中的数据:元组
            self.is_choice = True
            return SearchGroupRow(title, field_object.choices, self, request.GET)

    def get_text(self, field_object):
        """
        获取文本函数
        :param field_object:
        :return:
        """
        if self.text_func:
            return self.text_func(field_object)

        if self.is_choice:
            return field_object[1]

        return str(field_object)

    def get_value(self, field_object):
        if self.value_func:
            return self.value_func(field_object)

        if self.is_choice:
            return field_object[0]

        return field_object.pk

    def get_search_condition(self, request):
        if not self.is_condition:
            return None
        if self.is_multi:
            values_list = request.GET.getlist(self.field)  # tags=[1,2]
            if not values_list:
                return None
            return '%s__in' % self.field, values_list
        else:
            value = request.GET.get(self.field)  # tags=[1,2]
            if not value:
                return None
            return self.field, value

4. 分享案例

2709341-20230415001251195-92590572.png

优点:

  1. 可根据传入的字段名读取表中choice并展示和实现对应的筛选功能
  2. 大多数项目可以应用上
  3. 思路(django中admin的源码以及stark组件)

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK