Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/django/20.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Django过滤器:按模型属性过滤_Django_Properties_Django Queryset_Django Filter - Fatal编程技术网

Django过滤器:按模型属性过滤

Django过滤器:按模型属性过滤,django,properties,django-queryset,django-filter,Django,Properties,Django Queryset,Django Filter,我读到,不可能使用属性过滤Django查询集,因为Django ORM不知道如何将它们转换为SQL 但是,一旦数据被提取并加载到内存中,就可以使用这些属性在Python中对其进行过滤 我的问题是:是否有任何库允许查询集按内存中的属性进行过滤?如果没有,查询集必须如何被篡改才能实现?以及如何将django过滤器包括在其中?django过滤器希望并假设您正在使用queryset。一旦您获取一个查询集并将其更改为一个列表,那么下游的任何东西都需要能够只处理一个列表,或者只遍历列表,而列表不再是查询集

我读到,不可能使用属性过滤Django查询集,因为Django ORM不知道如何将它们转换为SQL

但是,一旦数据被提取并加载到内存中,就可以使用这些属性在Python中对其进行过滤


我的问题是:是否有任何库允许查询集按内存中的属性进行过滤?如果没有,查询集必须如何被篡改才能实现?以及如何将
django过滤器
包括在其中?

django过滤器
希望并假设您正在使用queryset。一旦您获取一个查询集并将其更改为一个
列表
,那么下游的任何东西都需要能够只处理一个
列表
,或者只遍历列表,而列表不再是查询集

如果您有一个
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')
class MyPropertyFilter(django_filters.Filter):
    def filter(self, qs, value):
        return [row for row in qs if row.baz == value]
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
然后您可以编写
MyPropertyFilter
如下所示:

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')
class MyPropertyFilter(django_filters.Filter):
    def filter(self, qs, value):
        return [row for row in qs if row.baz == value]
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
此时,
MyPropertyFilter
下游的任何内容都将有一个列表

注意:我认为
字段
的顺序应该有您的自定义筛选器,
MyPropertyFilter
最后一个,因为这样它将始终在常规queryset筛选器之后处理


因此,您刚刚破坏了“queryset”API,因为某些值是破坏的。在这一点上,你必须克服下游的错误。如果
FilterSet
之后的内容需要
.count
成员,您可以更改
MyPropertyFilter
如下:

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')
class MyPropertyFilter(django_filters.Filter):
    def filter(self, qs, value):
        return [row for row in qs if row.baz == value]
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
你在一个未知的领域,你将不得不通过黑客的方式


不管怎样,我以前做过,这并不可怕。错误来了就顺其自然。

你有没有困难的东西? 如果没有,可以将其重写为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)

可能这不是您想要的,但在某些情况下它可以帮助您

,因为通过非字段属性(如
属性
进行过滤不可避免地将
查询集
转换为
列表
(或类似),我喜欢推迟它,并在
获取上下文数据
方法中对
对象列表
进行过滤。为了将过滤逻辑保留在
filterset
类中,我使用了一个简单的技巧。我定义了一个
装饰器

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过滤器
非字段过滤方法。由于这个修饰符,过滤基本上不做(或跳过)非字段过滤方法(因为
force=False
默认值)

接下来,我定义了一个要在
视图
类中使用的
Mixin

    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\u filter\uu
的方法,这次使用
force=True

总之,您需要:

  • 视图
    类中继承
    过滤器YattrSmixin
  • 调用您的筛选方法
    attr\u filter\uu
  • 在筛选方法上使用
    attr\u filter
    decorator
简单示例(假设我有
模型
名为
MyModel
,具有
属性
名为
是静态的
,我想通过它进行过滤:

型号:

class MyModel(models.Model):
    ...

@property
def is_static(self):
    ...
视图:

过滤器:

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]
看看这个包。这是一个扩展,提供了按类属性过滤查询集的功能

文档中的简短示例:

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']

返回QuerySet而不是列表不是更好吗?当然返回QuerySet更好。但是你是说你不能。如果你不能对数据库进行筛选,那么你必须在之后进行筛选。QuerySet是绝对可以在数据库上执行的东西。如果你可以将属性更改为以某种方式执行在数据库中,您可以完全避免这种情况。但是,如果您必须在实际的python中进行过滤,那么您违反了queryset的本质,它不再是queryset。对不起,也许我没有很好地表达自己。我可以做一些类似于
返回queryset([row for row in qs If row.baz==value]的事情吗
这样它就有了所有的方法,比如
.count
.filter
等等?当然,它们都只在内存中工作,根本不会访问数据库,或者至少这是我的想法。我可以让它以某种方式工作吗?没有默认方法将
列表
更改回
查询集
.count
我在列表中很容易,但是
.filter()呢
?Django ORM不知道如何在
列表上进行过滤,它只在数据库上进行过滤。无论如何,下游代码正在使用的
QuerySet
API的每个部分都需要实现。如果您正确订购了过滤器,只需将
QuerySet
列表放入即可过滤器位于
字段的末尾。我想为所有类型的属性提供一个通用API,所以是的,我可能会有一些困难的属性。@karloss,我建议通用API是Django ORM本身。几乎每次通过Django ORM将过滤器推送到数据库时,它都会比加载对数据进行筛选,然后用python自己对其进行筛选。这不是因为python太慢,而是因为您正在从数据库中提取更多数据,而不是在数据库中对其进行筛选。编写自定义的
django_过滤器。对奇怪的推断属性进行筛选也没那么糟糕。@karloss,您还可以动态生成注释和过滤器。创建新的筛选器,然后使用filterset。我使用Django Rest Framework做了这项工作,效果非常好,目的是让客户端减少从服务器后端返回到自身的字段数。