使用MultipleChoiceFilter时动态重新加载选择

wKa*_*vey 6 django django-filters multiplechoicefield modelmultiplechoicefield

我正在尝试构建一个MultipleChoiceFilter选项,其中的选择是相关模型(DatedResource)上可能存在的日期的集合。

到目前为止,这是我正在使用的东西...

resource_date = filters.MultipleChoiceFilter(
    field_name='dated_resource__date',
    choices=[
        (d, d.strftime('%Y-%m-%d')) for d in
        sorted(resource_models.DatedResource.objects.all().values_list('date', flat=True).distinct())
    ],
    label="Resource Date"
)
Run Code Online (Sandbox Code Playgroud)

当它显示在html视图中时...

在此处输入图片说明

最初,这很好用,但是,如果我创建DatedResource具有新的不同date值的新对象,则需要重新启动我的Web服务器,以使它们成为此过滤器中的有效选择。我相信这是因为该choices列表是在Web服务器启动时进行一次评估的,而不是在每次我的页面加载时进行评估的。

有什么办法可以解决这个问题?也许通过一些创造性的使用ModelMultipleChoiceFilter

谢谢!

编辑: 我尝试了一些简单的ModelMultipleChoice用法,但遇到了一些问题。

resource_date = filters.ModelMultipleChoiceFilter(
    field_name='dated_resource__date',
    queryset=resource_models.DatedResource.objects.all().values_list('date', flat=True).order_by('date').distinct(),
    label="Resource Date"
)
Run Code Online (Sandbox Code Playgroud)

HTML表单显示得很好,但是选择不是过滤器可接受的值。我"2019-04-03" is not a valid value.假设收到验证错误,因为此过滤器需要datetime.date对象。我考虑过使用coerce参数,但是ModelMultipleChoice过滤器不接受这些参数。

根据狄克格罗滕的评论,我尝试使用链接问题中的建议。最终像

resource_date = filters.ModelMultipleChoiceFilter(
    field_name='dated_resource__date',
    to_field_name='date',
    queryset=resource_models.DatedResource.objects.all(),
    label="Resource Date"
)
Run Code Online (Sandbox Code Playgroud)

这也不是我想要的,因为HTML现在的形式是:a)显示str每个的表示形式DatedResource,而不是DatedResource.date字段; b)它们不是唯一的(例如,如果我有两个DatedResource对象相同date,则它们的两种str表示形式都出现在这也是不可持续的,因为我有200k + DatedResources,并且尝试全部加载时页面都挂起(与values_list过滤器相比,该过滤器可以在几秒钟内提取所有不同的日期。

JPG*_*JPG 5

一种简单的解决方案是重写__init__()filterset类的方法

from django_filters import filters, filterset


class FooFilter(filterset.FilterSet):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        try:
            self.filters['user'].extra['choices'] = [(d, d.strftime('%Y-%m-%d')) for d in sorted(
                resource_models.DatedResource.objects.all().values_list('date', flat=True).distinct())]
        except (KeyError, AttributeError):
            pass

    resource_date = filters.MultipleChoiceFilter(field_name='dated_resource__date', choices=[], label="Resource Date")
Run Code Online (Sandbox Code Playgroud)

注意:choices=[]在您的字段中提供filterset类的定义


结果

我使用以下依赖项测试并验证了此解决方案
。1. Python 3.6
2. Django 2.1
3. DRF 3.8.2
4. django-filter 2.0.0

我使用以下代码重现此行为。

# models.py
from django.db import models


class Musician(models.Model):
    name = models.CharField(max_length=50)

    def __str__(self):
        return f'{self.name}'


class Album(models.Model):
    artist = models.ForeignKey(Musician, on_delete=models.CASCADE)
    name = models.CharField(max_length=100)
    release_date = models.DateField()

    def __str__(self):
        return f'{self.name} : {self.artist}'


# serializers.py
from rest_framework import serializers


class AlbumSerializer(serializers.ModelSerializer):
    artist = serializers.StringRelatedField()

    class Meta:
        fields = '__all__'
        model = Album


# filters.py
from django_filters import rest_framework as filters


class AlbumFilter(filters.FilterSet):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.filters['release_date'].extra['choices'] = self.get_album_filter_choices()

    def get_album_filter_choices(self):
        release_date_list = Album.objects.values_list('release_date', flat=True).distinct()
        return [(date, date) for date in release_date_list]

    release_date = filters.MultipleChoiceFilter(choices=[])

    class Meta:
        model = Album
        fields = ('release_date',)


# views.py
from rest_framework.viewsets import ModelViewSet
from django_filters import rest_framework as filters


class AlbumViewset(ModelViewSet):
    serializer_class = AlbumSerializer
    queryset = Album.objects.all()
    filter_backends = (filters.DjangoFilterBackend,)
    filter_class = AlbumFilter
Run Code Online (Sandbox Code Playgroud)

在这里,我使用了django-filterwith DRF

现在,我通过Django管理控制台填充了一些数据。之后,专辑api如下所示,
专辑列表API结果
而我得到了release_date作为
日期选择之前

然后,我通过Django admin添加了一个新条目-(截图),然后刷新了DRF API端点,可能的选择如下所示,
FilterList的新选择