django 如何使用ModelMultipleChoiceFilter?

iswrvxsc  于 2022-11-19  发布在  Go
关注(0)|答案(2)|浏览(59)

我已经尝试让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的任何网站?

xxhby3vn

xxhby3vn1#

我在试图解决一个与你几乎相同的问题时偶然发现了这个问题,虽然我本可以只写一个自定义过滤器,但你的问题引起了我的兴趣,我不得不深入挖掘!
事实证明,ModelMultipleChoiceFilter只对普通的Filter进行了一次更改,如下面的django_filters源代码所示:

class ModelChoiceFilter(Filter):
    field_class = forms.ModelChoiceField

class ModelMultipleChoiceFilter(MultipleChoiceFilter):
    field_class = forms.ModelMultipleChoiceField

也就是说,它将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_filtersFilter.field()方法包括以下内容。

self._field = self.field_class(required=self.required,
    label=self.label, widget=self.widget, **self.extra)

特别值得注意的是**self.extra,它来自Filter.__init__()self.extra = kwargs,所以我们需要做的就是将一个额外的to_field_name kwarg传递给ModelMultipleChoiceFilter,它将被传递给底层的ModelMultipleChoiceField
因此(跳过此处查看实际的解决方案!),您需要的实际代码是

tags = django_filters.ModelMultipleChoiceFilter(
    name='sitetags__name',
    to_field_name='name',
    lookup_type='in',
    queryset=SiteTag.objects.all()
)

所以你真的很接近你上面张贴的代码!我不知道这个解决方案是否会与你相关了,但希望它可能会帮助别人在未来!

9wbgstp7

9wbgstp72#

对我有效的解决方案是使用MultipleChoiceFilter。在我的例子中,我有有种族的法官,我希望我的API让人们查询,比如说,黑人或白色法官。
过滤器最终为:

race = filters.MultipleChoiceFilter(
    choices=Race.RACES,
    action=lambda queryset, value:
        queryset.filter(race__race__in=value)
)

RaceJudge多对多字段:

class Race(models.Model):
    RACES = (
        ('w', 'White'),
        ('b', 'Black or African American'),
        ('i', 'American Indian or Alaska Native'),
        ('a', 'Asian'),
        ('p', 'Native Hawaiian or Other Pacific Islander'),
        ('h', 'Hispanic/Latino'),
    )
    race = models.CharField(
        choices=RACES,
        max_length=5,
    )

通常我并不太喜欢lambda函数,但它在这里很有意义,因为它是一个很小的函数。基本上,它设置了一个MultipleChoiceFilter,将GET参数的值传递到Race模型的race字段。它们以列表的形式传递,所以这就是in参数工作的原因。
因此,我的用户可以:

/api/judges/?race=w&race=b

他们会让那些被认定为黑人或白色的法官回来。
附言:是的,我知道这不是所有可能的种族。但它是美国人口普查收集的!

相关问题