【问题标题】:Apply filters to any or a certain many-to-many record将过滤器应用于任何或某个多对多记录
【发布时间】:2020-09-04 05:15:27
【问题描述】:

考虑这些模型:

from django.db import models

class Group(models.Model):
    name = models.CharField()

class Person(models.Model):
    height = models.PositiveIntegerField()
    weight = models.PositiveIntegerField()
    gender = models.CharField()
    groups = models.ManyToManyField(Group, blank=True)

和 DRF 视图

from rest_framework import viewsets
from rest_framework.filters import SearchFilter, OrderingFilter

from django_filters import rest_framework as filters

from .serializers import GroupSerializer
from ..models import Group

class GroupViewSet(viewsets.ModelViewSet):
    queryset = Group.objects.all().distinct()
    serializer_class = GroupSerializer
    filter_backends = (filters.DjangoFilterBackend,
                       SearchFilter, OrderingFilter)
    filter_class = GroupFilter

一个组可以有 0、1、2 个或更多 Persons,其中 1 和 2 是最常见的,其中 1 和 2 是明确定义的。将其视为 Facebook 的聊天:您最常进行一对一聊天,但有时您可以进行群聊。什么时候是一对一聊天,1是发送者,2是接收者。

我需要从 DRF 中过滤这些记录,在浏览 GroupViewSet 并按 Person 属性过滤时,我可以将一组过滤器应用于任何 Person 或某个 Person

对于任何人,无论适用于哪个特定条件,都是一目了然的:

/api/group/?person__height__gt=100&person__weight__gt=200

但是对于某个人,如果一组条件适用于那个人,在 URL 中,我可以有类似的内容:

/api/group/?person__0__height__gt=100&person__0__weight__gt=200&person__1__height__lte=200

并将这些声明到我的自定义FilterSet

from django.db.models.constants import LOOKUP_SEP

class GroupFilter(filters.FilterSet):
    person__0__height = filters.NumberFilter(method='person_filter')
    person__0__height__gt = filters.NumberFilter(method='person_filter')
    person__0__height__lt = filters.NumberFilter(method='person_filter')
    # ... and so on for the rest of the possibilities 

    def person_filter(self, queryset, name, value):
        m2mfield, index, field, *comparison = name.split(LOOKUP_SEP, 3)
        # do subqueries based on the above and construct queryset filter.

但正如您可以想象的那样,这意味着我将有很多样板代码。在我的真实模型中,有很多领域,上面的“解决方案”对我来说似乎很老套。

所以问题是:是否有更简单/更清洁的方法来实现上述过滤?

也许通过动态声明 person__0__height__gt 属性,我还没有找到解决方案。

请注意,我不知道 Person 实体的 ID。那些 person__0、person__1 是数组索引。

【问题讨论】:

    标签: django-rest-framework django-queryset django-filters


    【解决方案1】:

    试试这个清理代码:

    class GroupFilter(django_filters.FilterSet):
          person_range = django_filters.NumericRangeFilter(field_name='person__0__height', lookup_expr='range')
          person = django_filters.NumberFilter(field_name='person__0__height', lookup_expr='exact')
    
        class Meta:
              model = Group
              fields = ('person_range','person',)
    

    并像这样使用 url 调用: 127.0.0.1:8000/yourpath/?person=180&person_range_min=130&person_range_max=210

    【讨论】:

    • 谢谢,这大大简化了我的代码。我最终还是不得不传递一个method 参数,因为person__0__height 实际上并没有选择索引为0 的元素,所以我必须手动完成。仍然希望有一种方法可以动态声明这些字段。
    猜你喜欢
    • 2017-10-27
    • 2020-09-03
    • 1970-01-01
    • 1970-01-01
    • 2014-10-21
    • 1970-01-01
    • 1970-01-01
    • 2018-04-08
    • 1970-01-01
    相关资源
    最近更新 更多