使用MultipleChoiceFilter时动态重新加载选择 [英] Reload choices dynamically when using MultipleChoiceFilter

查看:169
本文介绍了使用MultipleChoiceFilter时动态重新加载选择的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am trying to construct a MultipleChoiceFilter where the choices are the set of possible dates that exist on a related model (DatedResource).

到目前为止,我正在与之合作...

Here is what I am working with so far...

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"
)

在html视图中显示时...

When this is displayed in a html view...

起初效果不错,但是,如果我创建具有新的date值的新的DatedResource对象,则需要重新启动我的Web服务器,以使其在此过滤器中作为有效的选择.我相信这是因为choices列表在网络服务器启动时被评估一次,而不是在每次页面加载时被评估.

This works fine at first, however if I create new DatedResource objects with new distinct date values I need to re-launch my webserver in order for them to get picked up as a valid choice in this filter. I believe this is because the choices list is evaluated once when the webserver starts up, not every time my page loads.

有什么办法可以解决这个问题?也许是通过创造性地使用ModelMultipleChoiceFilter来实现的?

Is there any way to get around this? Maybe through some creative use of a ModelMultipleChoiceFilter?

谢谢!

修改: 我尝试了一些简单的ModelMultipleChoice用法,但是遇到了一些问题.

I tried some simple ModelMultipleChoice usage, but hitting some issues.

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"
)

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

The HTML form is showing up just fine, however the choices are not accepted values to the filter. I get "2019-04-03" is not a valid value. validation errors, I am assuming because this filter is expecting datetime.date objects. I thought about using the coerce parameter, however those are not accepted in ModelMultipleChoice filters.

根据dirkgroten的评论,我尝试使用链接问题中的建议.最终像是

Per dirkgroten's comment, I tried to use what was suggested in the linked question. This ends up being something like

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

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

This also isnt what I want, as the HTML now form is now a) displaying the str representation of each DatedResource, instead of the DatedResource.date field and b) they are not unique (ex if I have two DatedResource objects with the same date, both of their str representations appear in the list. This also isnt sustainable because I have 200k+ DatedResources, and the page hangs when attempting to load them all (as compared to the values_list filter, which is able to pull all distinct dates out in seconds.

推荐答案

一种简单的解决方案是 覆盖filterset类的__init__()方法 . br>

One of the easy solutions will be overriding the __init__() method of the filterset class.

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")

注意:在您的过滤器集类别的字段定义中提供 choices=[]

NOTE: provide choices=[] in your field definition of filterset class

我已测试并验证了以下解决方案的依赖性
1. Python 3.6
2. Django 2.1
3. DRF 3.8.2
4. Django过滤器2.0.0

我使用以下代码重现了行为

I tested and verified this solution with following dependencies
1. Python 3.6
2. Django 2.1
3. DRF 3.8.2
4. django-filter 2.0.0

I used following code to reproduce the behaviour

# 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

在这里,我将 django-filter DRF 一起使用.

现在,我通过Django管理控制台填充了一些数据.之后,相册的api如下所示,

我得到 release_date


然后,我通过Django admin添加了新条目-(截屏),并刷新了DRF API端点和可能的选择如下,

Here I've used the django-filter with DRF.

Now, I populated some data through Django Admin console. After that, the album api become as below,

and I got the release_date as


Then, I added new entry through Django admin -- (Screenshot) and I refresh the DRF API endpoint and the possible choices became as below,

这篇关于使用MultipleChoiceFilter时动态重新加载选择的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆