DRF ModelMultipleChoiceFilter-许多参数之一中的无效值导致空列表

时间:2018-08-25 10:19:00

标签: python django django-rest-framework tagging

我有一个使用Django REST Framework构建的项目。我有模型ItemTag,它们之间存在多对多关系。请求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.pyviews.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

1 个答案:

答案 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']

希望这会有所帮助!