带有DRF的Django过滤器-使用相同的查找应用多个值时如何执行“和"操作? [英] Django-filter with DRF - How to do 'and' when applying multiple values with the same lookup?
问题描述
这是我正在使用的过滤器集的稍微简化的示例,我将其与Django Rest Framework的DjangoFilterBackend一起使用.我希望能够向/api/bookmarks/?title__contains=word1&title__contains=word2
发送请求并返回包含两个单词的结果,但是当前它忽略第一个参数,并且仅过滤word2.
This is a slightly simplified example of the filterset I'm using, which I'm using with the DjangoFilterBackend for Django Rest Framework. I'd like to be able to send a request to /api/bookmarks/?title__contains=word1&title__contains=word2
and have results returned that contain both words, but currently it ignores the first parameter and only filters for 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
推荐答案
主要问题是您需要一个了解如何对多个值进行运算的过滤器.基本上有两种选择:
The main problem is that you need a filter that understands how to operate on multiple values. There are basically two options:
- 使用
MultipleChoiceFilter
(此实例不推荐) - 编写自定义过滤器类
- Use
MultipleChoiceFilter
(not recommended for this instance) - Write a custom filter class
使用MultipleChoiceFilter
Using 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:
...
虽然这保留了您所需的语法,但问题是您必须构造一个选择列表.我不确定是否可以简化/减少可能的选择,但是袖手旁观似乎需要从数据库中获取所有标题,将标题拆分为不同的词,然后创建一个集合以删除重复项.根据您拥有的记录数量,这似乎是昂贵/缓慢的.
While this retains your desired syntax, the problem is that you have to construct a list of choices. I'm not sure if you can simplify/reduce the possible choices, but off the cuff it seems like you would need to fetch all titles from the database, split the titles into distinct words, then create a set to remove duplicates. This seems like it would be expensive/slow depending on how many records you have.
自定义Filter
Custom Filter
或者,您可以创建一个自定义过滤器类-类似于以下内容:
Alternatively, you can create a custom filter class - something like the following:
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:
...
用法(请注意,这些值以逗号分隔):
Usage (notice that the values are comma-separated):
GET /api/bookmarks/?title__contains=word1,word2
结果:
qs.filter(title__contains='word1').filter(title__contains='word2')
语法稍有变化,但是基于CSV的过滤器不需要构造不必要的选择集.
The syntax is changed a bit, but the CSV-based filter doesn't need to construct an unnecessary set of choices.
请注意,由于小部件无法呈现合适的html输入,因此实际上不可能支持?title__contains=word1&title__contains=word2
语法.您可能需要使用SelectMultiple
(再次需要选择),或在客户端上使用javascript添加/删除具有相同name
属性的其他文本输入.
Note that it isn't really possible to support the ?title__contains=word1&title__contains=word2
syntax as the widget can't render a suitable html input. You would either need to use SelectMultiple
(which again, requires choices), or use javascript on the client to add/remove additional text inputs with the same name
attribute.
无需赘述,过滤器和过滤器集只是Django形式的扩展.
Without going into too much detail, filters and filtersets are just an extension of Django's forms.
-
Filter
具有形式Field
,而形式又具有Widget
. -
FilterSet
由Filter
个组成. -
FilterSet
根据其过滤器的字段生成内部形式.
- A
Filter
has a formField
, which in turn has aWidget
. - A
FilterSet
is composed ofFilter
s. - A
FilterSet
generates an inner form based on its filters' fields.
每个过滤器组件的职责:
Responsibilities of each filter component:
- The widget retrieves the raw value from the
data
QueryDict
. - The field validates the raw value.
- The filter constructs the
filter()
call to the queryset, using the validated value.
为了对同一过滤器应用多个值,您将需要一个过滤器,字段和小部件,它们了解如何对多个值进行操作.
In order to apply multiple values for the same filter, you would need a filter, field, and widget that understand how to operate on multiple values.
自定义过滤器通过混入BaseCSVFilter
来实现此目的,该函数又将逗号分隔=>列表"功能混入组成的字段和窗口小部件类中.
The custom filter achieves this by mixing in BaseCSVFilter
, which in turn mixes in a "comma-separation => list" functionality into the composed field and widget classes.
我建议您查看CSV mixins的源代码,但总之:
I'd recommend looking at the source code for the CSV mixins, but in short:
- The widget splits the incoming value into a list of values.
- The field validates the entire list of values by validating individual values on the 'main' field class (such as
CharField
orIntegerField
). The field also derives the mixed in widget. - The filter simply derives the mixed in field class.
该CSV过滤器旨在与in
和range
查找一起使用,这些查找接受值列表.在这种情况下,contains
需要单个值. filter()
方法通过遍历值并将各个过滤器调用链接在一起来解决此问题.
The CSV filter was intended to be used with in
and range
lookups, which accept a list of values. In this case, contains
expects a single value. The filter()
method fixes this by iterating over the values and chaining together individual filter calls.
这篇关于带有DRF的Django过滤器-使用相同的查找应用多个值时如何执行“和"操作?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!