由于像property这样的非字段属性过滤不可避免地会将QuerySet转换为list(或类似的),所以我喜欢推迟它并在get_context_data方法中对object_list进行过滤。为了将过滤逻辑保留在filterset 类中,我使用了一个简单的技巧。我已经定义了一个decorator
def attr_filter(func):
def wrapper(self, queryset, name, value, force=False, *args, **kwargs):
if force:
return func(self, queryset, name, value, *args, **kwargs)
else:
return queryset
return wrapper
用于django-filter 非字段过滤方法。感谢这个装饰器,过滤基本上什么都不做(或跳过)非字段过滤方法(因为force=False默认值)。
接下来,我定义了一个Mixin 用于view 类。
class FilterByAttrsMixin:
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
filtered_list = self.filter_qs_by_attributes(self.object_list, self.filterset)
context.update({
'object_list': filtered_list,
})
return context
def filter_qs_by_attributes(self, queryset, filterset_instance):
if hasattr(filterset_instance.form, 'cleaned_data'):
for field_name in filter_instance.filters:
method_name = f'attr_filter_{field_name}'
if hasattr(filterset_instance, method_name):
value = filterset_instance.form.cleaned_data[field_name]
if value:
queryset = getattr(filterset_instance, filter_method_name)(queryset, field_name, value, force=True)
return queryset
它基本上只是返回到您的filterset 并运行所有称为attr_filter_<field_name> 的方法,这次是force=True。
总之,您需要:
- 在
view 类中继承FilterByAttrsMixin
- 调用你的过滤方法
attr_filter_<field_name>
- 在过滤方法上使用
attr_filter装饰器
简单示例(假设我有 model 称为 MyModel 和 property 称为 is_static 我想过滤:
型号:
class MyModel(models.Model):
...
@property
def is_static(self):
...
查看:
class MyFilterView(FilterByAttrsMixin, django_filters.views.FilterView):
...
filterset_class = MyFiltersetClass
...
过滤器:
class MyFiltersetClass(django_filters.FilterSet):
is_static = django_filters.BooleanFilter(
method='attr_filter_is_static',
)
class Meta:
model = MyModel
fields = [...]
@attr_filter
def attr_filter_is_static(self, queryset, name, value):
return [instance for instance in queryset if instance.is_static]