我已经尝试让ModelMultipleChoiceFilter工作了几个小时,并且已经阅读了DRF和Django过滤器文档。
我希望能够根据通过ManyToManyField分配给网站的标记筛选一组网站。例如,我希望能够获得标记为“烹饪”或“养蜂”的网站列表。
下面是我当前的www.example.com的相关片段models.py:
class SiteTag(models.Model):
"""Site Categories"""
name = models.CharField(max_length=63)
def __str__(self):
return self.name
class Website(models.Model):
"""A website"""
domain = models.CharField(max_length=255, unique=True)
description = models.CharField(max_length=2047)
rating = models.IntegerField(default=1, choices=RATING_CHOICES)
tags = models.ManyToManyField(SiteTag)
added = models.DateTimeField(default=timezone.now())
updated = models.DateTimeField(default=timezone.now())
def __str__(self):
return self.domain
还有我目前的views.py片段:
class WebsiteFilter(filters.FilterSet):
# With a simple CharFilter I can chain together a list of tags using &tag=foo&tag=bar - but only returns site for bar (sites for both foo and bar exist).
tag = django_filters.CharFilter(name='tags__name')
# THE PROBLEM:
tags = django_filters.ModelMultipleChoiceFilter(name='name', queryset=SiteTag.objects.all(), lookup_type="eq")
rating_min = django_filters.NumberFilter(name="rating", lookup_type="gte")
rating_max = django_filters.NumberFilter(name="rating", lookup_type="lte")
class Meta:
model = Website
fields = ('id', 'domain', 'rating', 'rating_min', 'rating_max', 'tag', 'tags')
class WebsiteViewSet(viewsets.ModelViewSet):
"""API endpoint for sites"""
queryset = Website.objects.all()
serializer_class = WebsiteSerializer
filter_class = WebsiteFilter
filter_backends = (filters.DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter,)
search_fields = ('domain',)
ordering_fields = ('id', 'domain', 'rating',)
我刚刚使用查询字符串[/path/to/sites]?tags=News
进行了测试,我100%确定在使用?tag
(缺少s
)查询时存在相应的记录(如所述)。
我尝试过的其他事情的一个例子是这样的:
tags = django_filters.ModelMultipleChoiceFilter(name='tags__name', queryset=Website.objects.all(), lookup_type="in")
如何返回SiteTag满足name == A OR name == B OR name == C
的任何网站?
2条答案
按热度按时间xxhby3vn1#
我在试图解决一个与你几乎相同的问题时偶然发现了这个问题,虽然我本可以只写一个自定义过滤器,但你的问题引起了我的兴趣,我不得不深入挖掘!
事实证明,
ModelMultipleChoiceFilter
只对普通的Filter
进行了一次更改,如下面的django_filters
源代码所示:也就是说,它将Django内置表单中的
field_class
更改为ModelMultipleChoiceField
。看一下
ModelMultipleChoiceField
的源代码,__init__()
的一个必需参数是queryset
,所以您的思路是正确的。另一块拼图来自
ModelMultipleChoiceField.clean()
方法,其中有一行代码:key = self.to_field_name or 'pk'
。这意味着,默认情况下,它将接受您传递给它的任何值(例如,"cooking"
),并尝试查找Tag.objects.filter(pk="cooking")
,而显然我们希望它查看名称,正如我们在该行中看到的,它与哪个字段进行比较由self.to_field_name
控制。幸运的是,在示例化实际字段时,
django_filters
的Filter.field()
方法包括以下内容。特别值得注意的是
**self.extra
,它来自Filter.__init__()
:self.extra = kwargs
,所以我们需要做的就是将一个额外的to_field_name
kwarg传递给ModelMultipleChoiceFilter
,它将被传递给底层的ModelMultipleChoiceField
。因此(跳过此处查看实际的解决方案!),您需要的实际代码是
所以你真的很接近你上面张贴的代码!我不知道这个解决方案是否会与你相关了,但希望它可能会帮助别人在未来!
9wbgstp72#
对我有效的解决方案是使用
MultipleChoiceFilter
。在我的例子中,我有有种族的法官,我希望我的API让人们查询,比如说,黑人或白色法官。过滤器最终为:
Race
是Judge
多对多字段:通常我并不太喜欢
lambda
函数,但它在这里很有意义,因为它是一个很小的函数。基本上,它设置了一个MultipleChoiceFilter
,将GET参数的值传递到Race
模型的race
字段。它们以列表的形式传递,所以这就是in
参数工作的原因。因此,我的用户可以:
他们会让那些被认定为黑人或白色的法官回来。
附言:是的,我知道这不是所有可能的种族。但它是美国人口普查收集的!