【问题标题】:DRF ModelMultipleChoiceFilter - invalid value in one of many params results in empty listDRF ModelMultipleChoiceFilter - 许多参数之一中的无效值导致空列表
【发布时间】:2019-01-31 15:09:40
【问题描述】:

我有一个使用 Django REST 框架构建的项目。我有模型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,我希望任何具有我请求的Tag 文本的任何Item 实例都包含在结果列表中。它似乎适用于数据库中记录的现有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

【问题讨论】:

    标签: python django django-rest-framework tagging


    【解决方案1】:

    我看了一下 django_filters 代码,似乎没有配置/选项可以让 ModelMultipleChoiceFilter 字段做你想做的事;过滤器使用的ModelMultipleChoiceField 使用Django 的ModelMultipleChoiceField 验证所有 相关标签的存在,其中:

    如果给定值无效(不是有效的 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']
    

    希望这会有所帮助!

    【讨论】:

      猜你喜欢
      • 2015-12-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-04-10
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多