django-rest-framework- 使用“或”过滤来自一个 url 参数的多个值

Posted

技术标签:

【中文标题】django-rest-framework- 使用“或”过滤来自一个 url 参数的多个值【英文标题】:django-rest-framework- Filtering using 'or' on multiple values from one url parameter 【发布时间】:2020-01-10 19:14:24 【问题描述】:

我为我的 API 公开的模型设置了一个标记系统。模型看起来像这样:

class TaggableModel(models.Model):
    name = models.CharField(max_length=255)
    tags = models.ManyToManyField(Tag, related_name="taggable_models")

class Tag(models.Model):
    tag = models.CharField(max_length=32)

然后我设置了一个序列化程序和视图,如下所示:

class TaggableModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = TaggableModel
        fields = ('id', 'name', 'tags',)
        read_only_fields = ('id',)

class TaggableModelViewSet(viewsets.ModelViewSet):
    queryset = TaggableModel.objects.all()
    serializer_class = TaggableModelSerializer
    permission_classes = (AllowAny,)
    filter_backend = [DjangoFilterBackend]
    filterset_fields = ['tags']

如果我想获取标签 ID 为 1、2 或 3 的所有 TaggableModels,我可以通过以下方式进行:

https://my-api-domain/api/taggable-models?tags=1&tags=2&tags=3

有没有办法在分隔符上拆分,所以我可以将它全部作为一个参数传递?例如:

https://my-api-domain/api/taggable-models?tags=1,2,3

看起来我可以编写自己的自定义 DjangoFilterBackend 过滤器,但我有点不确定从哪里开始。或者也许有更简单的方法可以做到这一点?

【问题讨论】:

【参考方案1】:

当然,您可以通过具有特定字段“小部件”的自定义过滤器集类来做到这一点(这就是它在 django-filters 中的调用方式)

您可以尝试以下示例:

# filters.py

from django_filters.rest_framework import FilterSet, filters
from django_filters.widgets import CSVWidget

from .your_models import Tag, TaggableModel

class TaggableModelFilterSet(FilterSet):
    tags = filters.ModelMultipleChoiceFilter(
        queryset=Tag.objects.all(), widget=CSVWidget,
        help_text=_("A list of ids, comma separated, identifying tags"),
        method='filter_tags'
    )

    class Meta:
        model = TaggableModel
        fields = ['tags']

    def filter_tags(self, queryset, name, value):
        if value:
            queryset = queryset.filter(tags__in=value)
        return queryset
# views.py

class TaggableModelViewSet(viewsets.ModelViewSet):
    queryset = TaggableModel.objects.all()
    serializer_class = TaggableModelSerializer
    permission_classes = (AllowAny,)
    filter_backends = [DjangoFilterBackend]
    filter_class = TaggableModelFilterSet

【讨论】:

谢谢你,它没有很好的记录。对于只想转换现有字段的人:使这项工作有效的关键设置是:指定 ModelMultipleChoiceFilter,包括对象的查询集和设置小部件。【参考方案2】:

使用django-filter package 有一种更简单的方法来实现此目的。在django-filter documentation 的深处,它提到您可以使用“映射到查找列表的字段名称字典”。

您的代码将像这样更新:

# views.py

from django_filters.rest_framework import DjangoFilterBackend

class TaggableModelViewSet(viewsets.ModelViewSet):
    queryset = TaggableModel.objects.all()
    serializer_class = TaggableModelSerializer
    permission_classes = (AllowAny,)
    filter_backend = [DjangoFilterBackend]
    filterset_fields = 
        'tags': ["in", "exact"] # note the 'in' field
    

现在在 URL 中,您可以在提供参数列表之前将 __in 添加到过滤器中,它会按您的预期工作:

https://my-api-domain/api/taggable-models?tags__in=1,2,3

关于可用查找过滤器的django-filter 文档非常差,但Django documentation 本身提到了in 查找过滤器。

【讨论】:

以上是关于django-rest-framework- 使用“或”过滤来自一个 url 参数的多个值的主要内容,如果未能解决你的问题,请参考以下文章

断言错误:Django-rest-Framework

如何仅使用 django 作为后端并使用 django-rest-framework 发布

为啥使用 django-rest-framework 时不需要 `csrf_exempt`?

django-rest-framework 是不是提供管理站点来管理模型?

无法使用视图名称 (django-rest-framework) 解析超链接关系的 URL

django-rest-framework:如何序列化已经包含 JSON 的字段?