问题的标准
前端是一种表单,需要在dict中给出跨多个模型的范围和内容的复杂查找。最好的办法吗?
解释
从视图中,我收到以下表格的字典(在被其他内容处理后):
{'h_index': {"min": 10,"max":20},
'rank' : "supreme_overlord",
'total_citations': {"min": 10,"max":400},
'year_began': {"min": 2000},
'year_end': {"max": 3000},
}
键是来自不同模型的列名(现在,2个单独的模型,Researcher和ResearchMetrics),值是我想要查询的范围/精确值。
示例(上图)
属于模型Researcher
:
属于模型ResearchMetrics
Researcher
与ResearchMetrics
有一对多的关系
Researcher
与Journals
(未提及相关内容)有多对多的关系
理想情况:我想在列表格式列表中向满足上述所有标准的研究人员展示。
Researcher ID, name, rank, year_began, year_end, total_citations, h_index
[[123, "Thomas", "professor", 2000, 2012, 15, 20],
[ 343 ... ]]
解决此问题的最佳方法是什么? (包括对表单的更改等?)我对整个表单查询模型的事情并不是很熟悉。
感谢您的帮助!
答案 0 :(得分:0)
要动态执行查询,您将dict
项目'fieldname__lookuptype': value
作为** kwargs传递给Model.objects.filter
。
因此,要在上面的示例中过滤rank
,year_began
和year_end
,您可以这样做:
转换的确切方式取决于此传入字典的变量。一个例子可能是这样的:
filter_in = {
'h_index': {"min": 10,"max":20},
'rank' : "supreme_overlord",
'total_citations': {"min": 10,"max":400},
'year_began': {"min": 2000},
'year_end': {"max": 3000},
}
LOOKUP_MAPPING = {
'min': 'gt',
'max': 'lt'
}
filter_kwargs = {}
for field in RESEARCHER_FIELDS:
if not field in filter_in:
continue
filter = filter_in[field]
if isinstance(filter, dict):
for filter_type, value in filter.items():
lookup_type = LOOKUP_MAPPING[filter_type]
lookup = '%s__%s' % (field, lookup_type)
filter_dict[lookup] = value
else:
filter_dict[field] = filter
这会生成如下字典:
{
'rank': 'supreme_overlord',
'year_began__gt': 2000,
'year_end__lt': 3000
}
像这样使用:
qs = Researcher.objects.filter(**filter_kwargs)
关于来自total_citations
的字段h_index
和ResearchMetrics
,我假设您要汇总这些值。因此,在上面的示例中,您需要总和或平均值。
原则是一样的:
from django.db.models import Sum
METRICS_FIELDS = ['total_citations', 'h_index']
annotate_kwargs = {}
for field in METRICS_FIELDS:
if not field in filter_in:
continue
annotated_field = '%s_sum' % field
annotate_kwargs[annotated_field] = Sum('researchmetric__%s' % field)
filter = filter_in[field]
if isinstance(filter, dict):
for filter_type, value in filter.items():
lookup_type = LOOKUP_MAPPING[filter_type]
lookup = '%s__%s' % (annotated_field, lookup_type)
filter_dict[lookup] = value
else:
filter_kwargs[field] = filter
现在你的filter_kwargs
看起来像这样:
{
'h_index_sum__gt': 10,
'h_index_sum__lt': 20,
'rank': 'supreme_overlord',
'total_citations_sum__gt': 10,
'total_citations_sum__lt': 400,
'year_began__gt': 2000,
'year_end__lt': 3000
}
你的annotate_kwargs
看起来像这样:
{
'h_index_sum': Sum('reasearchmetric__h_index')),
'total_citations_sum': Sum('reasearchmetric__total_citations'))
}
所以你的最终通话看起来像这样:
Researcher.objects.annotate(**annotate_kwargs).filter(**filter_kwargs)
我的答案中有一些假设,但我希望你能得到一般的想法。
有一点很重要:确保正确验证输入,以确保只过滤您希望用户过滤的字段。在我的方法中,通过对RESEARCHER_FIELDS
和METRICS_FIELDS
中的字段名称进行硬编码来确保这一点。