将过滤器应用于任何或特定的多对多记录
Apply filters to any or a certain many-to-many record
考虑这些模型:
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.
但是如您所想,这意味着我将拥有大量样板代码。在我的真实模型中有很多字段,上面的 "solution" 对我来说似乎很老套。
那么问题来了:有没有easier/cleaner的方法可以实现上面的过滤?
也许可以通过动态声明 person__0__height__gt
属性,对此我还找不到解决方案。
请注意,我事先不知道 Person
个实体的 ID。 person__0、person__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
考虑这些模型:
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.
但是如您所想,这意味着我将拥有大量样板代码。在我的真实模型中有很多字段,上面的 "solution" 对我来说似乎很老套。
那么问题来了:有没有easier/cleaner的方法可以实现上面的过滤?
也许可以通过动态声明 person__0__height__gt
属性,对此我还找不到解决方案。
请注意,我事先不知道 Person
个实体的 ID。 person__0、person__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