使用DRF的Django过滤器 - 当使用相同的查找应用多个值时,如何做'和'?

erg*_*sto 2 python django django-filter django-filters django-rest-framework

这是我正在使用的filterset的一个略微简化的例子,我正在使用DjangoFilterBackend for Django Rest Framework.我希望能够发送请求/api/bookmarks/?title__contains=word1&title__contains=word2并返回包含两个单词的结果,但是当前它忽略了第一个参数并且只忽略了word2的过滤器.

任何帮助将非常感谢!

class BookmarkFilter(django_filters.FilterSet):

    class Meta:
        model = Bookmark
        fields = {
            'title': ['startswith', 'endswith', 'contains', 'exact', 'istartswith', 'iendswith', 'icontains', 'iexact'],
        }

class BookmarkViewSet(viewsets.ModelViewSet):
    serializer_class = BookmarkSerializer
    permission_classes = (IsAuthenticated,)
    filter_backends = (DjangoFilterBackend,)
    filter_class = BookmarkFilter
    ordering_fields = ('title', 'date', 'modified')
    ordering = '-modified'
    page_size = 10
Run Code Online (Sandbox Code Playgroud)

She*_*rpa 6

主要问题是您需要一个了解如何操作多个值的过滤器.基本上有两种选择:

  • 使用MultipleChoiceFilter(不推荐用于此实例)
  • 编写自定义过滤器类

运用 MultipleChoiceFilter

class BookmarkFilter(django_filters.FilterSet):
    title__contains = django_filters.MultipleChoiceFilter(
        name='title',
        lookup_expr='contains',
        conjoined=True,  # uses AND instead of OR
        choices=[???],
    )

    class Meta:
        ...
Run Code Online (Sandbox Code Playgroud)

虽然这保留了您想要的语法,但问题是您必须构建一个选择列表.我不确定你是否可以简化/减少可能的选择,但是从袖口看起来你需要从数据库中获取所有标题,将标题拆分成不同的单词,然后创建一个集来删除重复项.根据您拥有的记录数量,这似乎很昂贵/慢.

习惯 Filter

或者,您可以创建自定义过滤器类 - 如下所示:

class MultiValueCharFilter(filters.BaseCSVFilter, filters.CharFilter):
    def filter(self, qs, value):
        # value is either a list or an 'empty' value
        values = value or []

        for value in values:
            qs = super(MultiValueCharFilter, self).filter(qs, value)

        return qs


class BookmarkFilter(django_filters.FilterSet):
    title__contains = MultiValueCharFilter(name='title', lookup_expr='contains')

    class Meta:
        ...
Run Code Online (Sandbox Code Playgroud)

用法(注意值以逗号分隔):

GET /api/bookmarks/?title__contains=word1,word2
Run Code Online (Sandbox Code Playgroud)

结果:

qs.filter(title__contains='word1').filter(title__contains='word2')
Run Code Online (Sandbox Code Playgroud)

语法稍有改变,但基于CSV的过滤器不需要构造一组不必要的选择.

请注意,?title__contains=word1&title__contains=word2由于窗口小部件无法呈现合适的html输入,因此无法支持语法.您可能需要使用SelectMultiple(这需要再次选择),或者在客户端上使用javascript来添加/删除具有相同name属性的其他文本输入.


不过多详细介绍,过滤器和过滤器只是Django表单的扩展.

  • A Filter有一个形式Field,而这个形式又有一个Widget.
  • A FilterSetFilters 组成.
  • A FilterSet根据其过滤器字段生成内部表单.

每个过滤器组件的责任:

  • 小部件从中检索原始值data QueryDict.
  • 该字段验证原始值.
  • 过滤器filter()使用经过验证的值构造对查询集的调用.

要为同一过滤器应用多个值,您需要一个过滤器,字段和小部件,以了解如何对多个值进行操作.


自定义过滤器通过混合实现这BaseCSVFilter一点,然后将"逗号分隔=>列表"功能混合到组合字段和窗口小部件类中.

我建议查看CSV mixins的源代码,但简而言之:

  • 控件将传入值转换值的列表.
  • 字段通过验证"main"字段类(例如CharFieldIntegerField)上的各个值来验证整个值列表.该字段还派生混合窗口小部件.
  • 过滤器简单地推导出混合的领域类.

该CSV滤波器的目的是与使用inrange查找,其接受的值的列表.在这种情况下,contains需要一个值.该filter()方法通过迭代值并将各个过滤器调用链接在一起来解决此问题.