动态构建django-filter FilterSet 一个动态构建FilterSet的通用方法可以用在api_view等比较灵活的场景当中# utils/dynamic_filter.py from typing import Type, Union from django.db.models import QuerySet from django_filters import rest_framework as filters from django.db import models from django.db.models import Model from functools import lru_cache def get_filter_config(model_field, field_name): 根据字段类型返回对应的过滤器配置 规则 - 字符类型 → icontains模糊搜索 - 外键/一对一 → 精确匹配 - 数字类型 → 精确匹配 - 布尔类型 → 精确匹配 - 日期时间 → 支持范围查询 - 多对多 → 包含查询 - UUID → 精确匹配 field_type type(model_field) if field_type in [models.CharField, models.TextField, models.EmailField, models.URLField, models.SlugField]: # 主键、外键和一对一用精确匹配 if model_field.primary_key or isinstance(model_field, (models.ForeignKey, models.OneToOneField)): if isinstance(model_field, (models.ForeignKey, models.OneToOneField)): return filters.ModelChoiceFilter( field_namefield_name, querysetmodel_field.related_model.objects.all() ) # 主键使用精确匹配 return filters.CharFilter(field_namefield_name, lookup_exprexact) return filters.CharFilter(field_namefield_name, lookup_expricontains) elif field_type in [models.IntegerField, models.FloatField, models.DecimalField, models.PositiveIntegerField, models.SmallIntegerField, models.BigIntegerField]: return filters.NumberFilter(field_namefield_name) elif field_type models.BooleanField: return filters.BooleanFilter(field_namefield_name) elif field_type in [models.DateTimeField, models.DateField]: return filters.DateTimeFilter(field_namefield_name) elif field_type models.ForeignKey: return filters.CharFilter(field_namefield_name, lookup_exprexact) # 多对多 elif field_type models.ManyToManyField: return filters.ModelMultipleChoiceFilter( field_namefield_name, querysetmodel_field.related_model.objects.all(), ) # UUID elif field_type models.UUIDField: return filters.UUIDFilter(field_namefield_name) # 默认 else: return filters.CharFilter(field_namefield_name) lru_cache(maxsize128) def create_dynamic_filterset(model, filter_fields(), exclude_fields(), search_fields(), search_paramsearch, ordering_fields(), ordering_paramorder_by): 根据模型和字段列表动态创建 FilterSet支持过滤 排序 搜索 Args: model: Django模型类 filter_fields: 需要过滤的字段列表None表示所有字段 exclude_fields: 排除的字段列表 ordering_fields: 允许排序的字段列表None表示所有字段 ordering_param: 前端传递排序参数的key默认 ordering search_fields: 允许搜索的字段列表 search_param: 前端传递搜索参数的key默认 search Returns: FilterSet: 动态生成的FilterSet类 filter_fields list(filter_fields) if filter_fields else None exclude_fields list(exclude_fields) if exclude_fields else None search_fields list(search_fields) if search_fields else None ordering_fields list(ordering_fields) if ordering_fields else None if filter_fields is None: filter_fields [ f.name for f in model._meta.get_fields() if f.concrete and not f.auto_created ] if exclude_fields: filter_fields [f for f in filter_fields if f not in exclude_fields] # 构建过滤器字典 filters_dict {} for field_name in filter_fields: try: model_field model._meta.get_field(field_name) filter_config get_filter_config(model_field, field_name) if filter_config: filters_dict[field_name] filter_config except models.FieldDoesNotExist: continue # 确定排序字段 if ordering_fields is None: ordering_fields [] if ordering_fields: filters_dict[ordering_param] filters.CharFilter( field_nameordering, methodfilter_ordering, label排序 ) # 确定搜索字段 手动指定 # if search_fields is None: # # 默认搜索所有字符类型字段排除外键 # search_fields [ # f.name for f in model._meta.get_fields() # if f.concrete and not f.auto_created # and isinstance(f, (models.CharField, models.TextField, # models.EmailField, models.URLField, models.SlugField)) # ] # # 如果没有字符字段添加主键作为搜索字段 # if not search_fields: # search_fields [id] # 添加搜索过滤器 search_fields_list [] if search_fields: filters_dict[search_param] filters.CharFilter( field_namesearch, methodfilter_search, label搜索 ) search_fields_list search_fields # 创建 Meta 类 meta_class type( Meta, (), # 父类元组 { model: model, fields: list(filters_dict.keys()) } ) # 创建 FilterSet 类 class_attrs filters_dict.copy() class_attrs[Meta] meta_class def filter_ordering(self, queryset, name, value): 自定义排序方法 if not value: return queryset primary_key model._meta.pk.name order_fields [] for field in value.split(,): field field.strip() if not field: continue if field.startswith(-): order_field field[1:] is_desc True else: order_field field is_desc False if order_field in ordering_fields: if is_desc: order_fields.append(f-{order_field}) else: order_fields.append(order_field) if order_fields: return queryset.order_by(*order_fields) else: return queryset.order_by(f-{primary_key}) class_attrs[filter_ordering] filter_ordering class_attrs[_ordering_fields] ordering_fields class_attrs[_ordering_param] ordering_param # 添加搜索方法 def filter_search(self, queryset, name, value): 自定义搜索方法 if not value: return queryset # 构建 OR 查询 from django.db.models import Q q_objects Q() for field_name in search_fields_list: # 支持跨表搜索双下划线 if __ in field_name: q_objects | Q(**{f{field_name}__icontains: value}) else: # 检查字段是否存在 try: model._meta.get_field(field_name) q_objects | Q(**{f{field_name}__icontains: value}) except models.FieldDoesNotExist: # 忽略不存在的字段 continue return queryset.filter(q_objects) class_attrs[filter_search] filter_search class_attrs[_search_fields] search_fields_list class_attrs[_search_param] search_param # 创建类 filter_class type( f{model.__name__}DynamicFilter, (filters.FilterSet,), class_attrs ) return filter_class def build_query(request, model: Type[Model], queryset: QuerySet None, filter_fields: Union[list, tuple] (), exclude_fields: Union[list, tuple] (), search_fields: Union[list, tuple] (), search_paramsearch, ordering_fields: Union[list, tuple] (), ordering_paramorder_by): 过滤和排序queryset 传入queryset从queryset中查否则从model中查 filter_fields 过滤字段动态构建过滤器 search_fields 模糊搜索字段 前端需传参 ?search... ordering_fields 排序字段 ordering_param排序关键字 FilterClass create_dynamic_filterset(model, tuple(filter_fields), tuple(exclude_fields), tuple(search_fields), search_param, tuple(ordering_fields), ordering_param) queryset model.objects.all() if not queryset else queryset filterset FilterClass(datarequest.query_params, querysetqueryset, requestrequest) if not filterset.is_valid(): return None return filterset.qs