将过滤器应用于任何或某一多对多的记录。

问题描述 投票:0回答:1

考虑这些模式。

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或更多的人 Person的,其中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 属性,我还找不到解决方法。

需要注意的是,我不知道这些属性的ID。Person 前面的实体。那些person__0、person__1是数组索引。

django-rest-framework django-queryset django-filters
1个回答
2
投票

试试这个清理代码。

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:8000yourpath?person=180&person_range_min=130&person_range_max=210。

© www.soinside.com 2019 - 2024. All rights reserved.