【发布时间】:2020-09-25 19:11:48
【问题描述】:
我在我的项目中使用 Django 过滤器 (django-filter)。我有以下模型,其中一个组合 (Work) 有一个多对多 instrumentations 字段和一个 through 模型。每个仪器中都有几个仪器。
models.py:
class Work(models.Model):
instrumentations = models.ManyToManyField(Instrument,
through='Instrumentation',
blank=True)
class Instrument(models.Model):
name = models.CharField(max_length=100)
class Instrumentation(models.Model):
players = models.IntegerField(validators=[MinValueValidator(1)])
work = models.ForeignKey(Work, on_delete=models.CASCADE)
instrument = models.ForeignKey(Instrument, on_delete=models.CASCADE)
views.py:
import django_filters
class WorkFilter(django_filters.FilterSet):
instrument = django_filters.ModelMultipleChoiceFilter(
field_name="instrumentation__instrument",
queryset=Instrument.objects.all())
我的过滤器工作正常:它会抓取用户在过滤器表单中选择的乐器的所有部分。
但是,我想添加使用这些精确工具过滤乐曲的可能性。例如,如果一首乐曲包含小提琴、圆号和大提琴而没有其他内容,我想得到它,但不是为小提琴、圆号、大提琴和打击乐而写的乐曲。有可能实现吗?
我还希望用户从界面中选择是否执行精确搜索,但我想这是目前的次要问题。
更新:type_of_search使用ChoiceFilter
我取得了一些的进步;使用下面的代码,我可以让用户在两种搜索之间进行选择。现在,我需要找出哪个查询将只抓取具有该组确切乐器的乐曲。
class WorkFilter(django_filters.FilterSet):
# ...
CHOICES = {
('exact', 'exact'), ('not_exact', 'not_exact')
}
type_of_search = django_filters.ChoiceFilter(label="Exact match?", choices=CHOICES, method="filter_instruments")
def filter_instruments(self, queryset, name, value):
if value == 'exact':
return queryset.??
elif value == 'not_exact':
return queryset.??
我知道我想要的查询类似于:
Work.objects.filter(instrumentations__name='violin').filter(instrumentations__name='viola').filter(instrumentations__name='horn')
我只是不知道如何将其“翻译”成django_filters 语言。
更新2:'exact'查询使用QuerySet.annotate
感谢this question,我想这是我正在寻找的查询:
from django.db.models import Count
instrument_list = ['...'] # How do I grab them from the form?
instruments_query = Work.objects.annotate(count=Count('instrumentations__name')).filter(count=len(instrument_list))
for instrument in instrument_list:
instruments_query = instruments_query.filter(instrumentations__name=instrument_list)
我觉得我很接近,我只是不知道如何与django_filters集成。
更新 3:WorkFilter 如果搜索准确则返回空
class WorkFilter(django_filters.FilterSet):
genre = django_filters.ModelChoiceFilter(
queryset=Genre.objects.all(),
label="Filter by genre")
instrument = django_filters.ModelMultipleChoiceFilter(
field_name="instrumentation__instrument",
queryset=Instrument.objects.all(),
label="Filter by instrument")
CHOICES = {
('exact', 'exact'), ('not_exact', 'not_exact')
}
type_of_search = django_filters.ChoiceFilter(label="Exact match?", choices=CHOICES, method="filter_instruments")
def filter_instruments(self, queryset, name, value):
instrument_list = self.data.getlist('instrumentation__instrument')
if value == 'exact':
queryset = queryset.annotate(count=Count('instrumentations__name')).filter(count=len(instrument_list))
for instrument in instrument_list:
queryset = queryset.filter(instrumentations__name=instrument)
elif value == 'not_exact':
pass # queryset = ...
return queryset
class Meta:
model = Work
fields = ['genre', 'title', 'instrument', 'instrumentation']
【问题讨论】:
标签: django django-views django-templates django-filter django-filters