问题描述
我正在尝试构造一个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)显示每个DatedResource
的str
表示形式,而不是DatedResource.date
字段; b)它们不是唯一的(例如,如果我有两个具有相同date
的DatedResource
对象,它们的两个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时动态重新加载选择的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!