我在several places上读到,无法使用属性过滤Django查询集,因为Django ORM不知道如何将其转换为SQL。
但是,一旦将数据提取并加载到内存中,就可以 使用这些属性在Python中对其进行过滤。
我的问题是:是否有任何库允许查询集按内存中的属性过滤?如果不是,那么如何精确地篡改查询集才能使之成为可能?以及如何将django-filter
包含在其中?
答案 0 :(得分:1)
django-filter
想要并假设您正在使用查询集。一旦您获取了一个查询集并将其更改为list
,那么下游的所有内容都将只能处理list
或仅遍历列表,而不再是查询集。
如果您有django_filters.FilterSet
,例如:
class FooFilterset(django_filters.FilterSet):
bar = django_filters.Filter('updated', lookup_expr='exact')
my_property_filter = MyPropertyFilter('property')
class Meta:
model = Foo
fields = ('bar', 'my_property_filter')
然后您可以像这样写MyPropertyFilter
:
class MyPropertyFilter(django_filters.Filter):
def filter(self, qs, value):
return [row for row in qs if row.baz == value]
这时,MyProperteyFilter
下游的所有内容都会有一个列表。
注意:我相信fields
的顺序应该有您的自定义过滤器,MyPropertyFilter
最后,因为这样,它将始终在常规查询集过滤器之后进行处理。 >
因此,对于某些损坏的值,您刚刚破坏了“ queryset” API。在这一点上,您必须解决所有下游错误。如果FilterSet
之后需要.count
成员,则可以像这样更改MyPropertyFilter
:
class MyPropertyFilter(django_filters.Filter):
def filter(self, qs, value):
result = [row for row in qs if row.baz == value]
result.count = len(result)
return result
您处在未知的领域,您将不得不破解自己的方式。
无论如何,我之前已经做过,这并不可怕。只需解决错误,就可以解决。
答案 1 :(得分:1)
您有没有困难的财产? 如果没有,您可以像这样将其重写为queryset:
from django.db import models
class UserQueryset(models.Manager):
def get_queryset(self):
return super().get_queryset().annotate(
has_profile=models.Exists(Profile.objects.filter(user_id=models.OuterRef('id')))
)
class User(models.Model):
objects = UserQueryset
class Profile(models.Model):
user = models.OneToOneField(User, related_name='profile')
# When you want to filter by has profile just use it like has field has profile
user_with_profiles = User.objects.filter(has_profile=True)
也许不是您想要的,但是在某些情况下它可以为您提供帮助
答案 2 :(得分:1)
由于通过非字段属性(例如property
进行过滤不可避免地将QuerySet
转换为list
(或类似的结果),因此我希望将其推迟并在{{1}上进行过滤} object_list
方法中。为了将过滤逻辑保留在get_context_data
类中,我使用了一个简单的技巧。我定义了一个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
类中的FilterByAttrsMixin
view
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
...
答案 3 :(得分:0)
看看 django-property-filter 包。这是 django-filter 的扩展,提供按类属性过滤查询集的功能。
文档中的简短示例:
from django_property_filter import PropertyNumberFilter, PropertyFilterSet
class BookFilterSet(PropertyFilterSet):
prop_number = PropertyNumberFilter(field_name='discounted_price', lookup_expr='gte')
class Meta:
model = NumberClass
fields = ['prop_number']