首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Django-filter:按模型属性过滤

Django-filter:按模型属性过滤
EN

Stack Overflow用户
提问于 2019-04-02 20:38:04
回答 4查看 6.3K关注 0票数 3

我继续读下去severalplaces不可能使用属性过滤Django查询集,因为Django ORM不知道如何将这些转换为SQL。

但是,一旦数据被读取并加载到内存中,它应可以使用这些属性在Python中过滤它们。

我的问题是:有没有库允许根据内存中的属性过滤查询集?如果不是,那么查询集到底应该如何篡改才能实现呢?以及如何包含django-filter变成这样?

EN

回答 4

Stack Overflow用户

发布于 2019-04-02 20:58:58

你有没有困难的财产?如果没有,您可以将其重写为queryset,如下所示:

代码语言:javascript
复制
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)

也许它不是你想要的,但在某些情况下它可以帮助你。

票数 3
EN

Stack Overflow用户

发布于 2019-04-02 20:55:27

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

如果你有一个django_filters.FilterSet例如:

代码语言:javascript
复制
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例如:

代码语言:javascript
复制
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,对于某些值而言,就是broken。在这一点上,您将不得不解决下游的任何错误。如果在FilterSet需要一个.count成员,您可以更改MyPropertyFilter例如:

代码语言:javascript
复制
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
EN

Stack Overflow用户

发布于 2020-09-09 22:00:16

由于按非字段属性进行过滤,例如property不可避免地将QuerySetlist(或类似的),我喜欢推迟它,并在上面做过滤object_listget_context_data方法。将过滤逻辑保留在filterset类中,我使用了一个简单的技巧。我定义了一个decorator

代码语言:javascript
复制
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类。

代码语言:javascript
复制
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_,这一次使用force=True

总之,您需要:

  • 继承FilterByAttrsMixin在您的view
  • 调用你的过滤方法attr_filter_
  • 使用attr_filter过滤方法上的装饰器

简单的例子(鉴于我有model被称为MyModel使用property被称为is_static我想通过以下方式进行过滤:

型号:

代码语言:javascript
复制
class MyModel(models.Model):
    ...

@property
def is_static(self):
    ...

查看:

代码语言:javascript
复制
class MyFilterView(FilterByAttrsMixin, django_filters.views.FilterView):
    ...
    filterset_class = MyFiltersetClass
    ...

过滤器:

代码语言:javascript
复制
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]
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/55474992

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档