我有一个使用Django REST Framework构建的项目。我有模型Item
和Tag
,它们之间存在多对多关系。请求Item
实例列表时,我正在使用ModelMultipleChoiceFilter
按标签过滤Item
列表。
这是我的filters.py
:
import django_filters
from .models import Item, Tag
class ItemTagFilter(django_filters.FilterSet):
tags = django_filters.ModelMultipleChoiceFilter(name='tags__text',
to_field_name='text',
queryset=Tag.objects.all(),
conjoined=False,)
class Meta:
model = Item
fields = ['tags']
您可能会注意到,由于默认情况下conjoined
的值为False
,所以我希望将包含我要求的任何Item
文本的任何Tag
实例包含在其中结果列表。它似乎适用于数据库中记录的现有Tag
实例。
问题是,当我输入不存在的Tag
文本时,即使我已经发送了多个Tag
文本,并且它们确实存在于数据库中,也返回了一个空列表。 (即,我希望过滤器返回Item
元素的并集,这些元素具有我在请求中发送的任何标签)
我调查了Django REST Framework documentation和一些相关的SO帖子,例如this,但我既找不到问题的根本原因,也没有找到解决方案。我将不胜感激。
如果需要更多信息,可以在下面找到我的models.py
和views.py
。
models.py:
from django.db import models
class Tag(models.Model):
text = models.CharField(max_length = 100, unique = True)
...
class Item(models.Model):
info = models.CharField(max_length = 200)
tags = models.ManyToManyField(Tag, related_name='items')
views.py :
from rest_framework import generics
from .models import Item
from .filters import ItemTagFilter
import django_filters.rest_framework as filters
...
class ListCreateItemView(generics.ListCreateAPIView):
queryset = Item.objects.all()
filter_backends = (filters.DjangoFilterBackend,)
filter_class = ItemTagFilter
serializer_class = ItemSerializer
答案 0 :(得分:0)
我稍微看了看django_filters代码,似乎没有任何配置/选项可以使ModelMultipleChoiceFilter
字段执行您想要的操作;过滤器使用的ModelMultipleChoiceField
使用Django的ModelMultipleChoiceField
来验证 all 相关标签的存在,
如果给定值无效(不是有效的PK,不在查询集中等),请引发ValidationError
这意味着,如果提供的任何标签无效,则您从中选择用于过滤的标签的列表将为空。
但是,您可以通过重写ModelMultipleChoiceField
_check_values
方法并自己计算查询集来绕过此操作。我认为类似这样的方法应该起作用:
class CustomField(django_filters.fields.ModelMultipleChoiceField):
def _check_values(self, value):
"""
Override the base class' _check_values method so our queryset is not
empty if one of the items in value is invalid.
"""
null = self.null_label is not None and value and self.null_value in value
if null:
value = [v for v in value if v != self.null_value]
field_name = self.to_field_name or 'pk'
result = list(self.queryset.filter(**{'{}__in'.format(field_name): value}))
result += [self.null_value] if null else []
return result
class CustomModelMultipleChoiceFilter(django_filters.ModelMultipleChoiceFilter):
field_class = CustomField
class ItemTagFilter(django_filters.FilterSet):
tags = CustomModelMultipleChoiceFilter(name='tags__text',
to_field_name='text',
queryset=Tag.objects.all(),
conjoined=False,)
class Meta:
model = Item
fields = ['tags']
希望这会有所帮助!