多字段的 Django 过滤器完全匹配:ManyToManyField 使用 ModelMultipleChoiceFilter
Django filter exact match for multi field: ManyToManyField using ModelMultipleChoiceFilter
我在我的项目中使用 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')
我只是不知道如何 'translate' 将其翻译成 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
如果搜索准确 returns 为空
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']
你可以用 self.data.getlist('instrument')
抢到 instrument_list
。
这就是您将 instrument_list
用于 'exact'
查询的方式:
type_of_search = django_filters.ChoiceFilter(label="Exact match?", choices=CHOICES, method=lambda queryset, name, value: queryset)
instrument = django_filters.ModelMultipleChoiceFilter(
field_name="instrumentation__instrument",
queryset=Instrument.objects.all(),
label="Filter by instrument",
method="filter_instruments")
def filter_instruments(self, queryset, name, value):
if not value:
return queryset
instrument_list = self.data.getlist('instrument') # [v.pk for v in value]
type_of_search = self.data.get('type_of_search')
if type_of_search == 'exact':
queryset = queryset.annotate(count=Count('instrumentations')).filter(count=len(instrument_list))
for instrument in instrument_list:
queryset = queryset.filter(instrumentations__pk=instrument)
else:
queryset = queryset.filter(instrumentations__pk__in=instrument_list).distinct()
return queryset
我在我的项目中使用 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')
我只是不知道如何 'translate' 将其翻译成 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
如果搜索准确 returns 为空
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']
你可以用 self.data.getlist('instrument')
抢到 instrument_list
。
这就是您将 instrument_list
用于 'exact'
查询的方式:
type_of_search = django_filters.ChoiceFilter(label="Exact match?", choices=CHOICES, method=lambda queryset, name, value: queryset)
instrument = django_filters.ModelMultipleChoiceFilter(
field_name="instrumentation__instrument",
queryset=Instrument.objects.all(),
label="Filter by instrument",
method="filter_instruments")
def filter_instruments(self, queryset, name, value):
if not value:
return queryset
instrument_list = self.data.getlist('instrument') # [v.pk for v in value]
type_of_search = self.data.get('type_of_search')
if type_of_search == 'exact':
queryset = queryset.annotate(count=Count('instrumentations')).filter(count=len(instrument_list))
for instrument in instrument_list:
queryset = queryset.filter(instrumentations__pk=instrument)
else:
queryset = queryset.filter(instrumentations__pk__in=instrument_list).distinct()
return queryset