【问题标题】:Django complex query based on dicts基于dicts的django复杂查询
【发布时间】:2017-09-20 13:19:50
【问题描述】:

问题标准

前端是一种形式,它需要在字典中给出跨多个模型的范围和内容进行复杂查找。最好的方法?

说明

从视图中,我收到以下形式的字典(经过其他处理后):

{'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

  • 排名
  • year_began
  • year_end

属于模特ResearchMetrics

  • 总引用次数
  • h_index

ResearcherResearchMetrics 具有一对多关系 ResearcherJournals 具有多对多关系(未在问题中提及)

理想情况下:我想以列表格式向满足上述所有标准的研究人员展示。

Researcher ID, name, rank, year_began, year_end, total_citations, h_index

[[123, "Thomas", "professor", 2000, 2012, 15, 20],
[ 343 ...                                   ]]

解决这个问题的最佳方法是什么? (包括对表单的更改等?)我对整个表单查询模型不是很熟悉。

感谢您的帮助!

【问题讨论】:

  • ResearcherResearchMetrics 有什么关系?你能告诉我们你的模型定义吗?
  • @DanielHepper 将更新问题,他们有一对多的关系
  • 如果这是关于学习基础知识,那么至少要阅读 Django 文档中有关模型、视图、表单和模板的介绍文档。如果这是为了解决问题,那么been solved 已经为您服务了。
  • 绝对是关于一个特定问题,我确实浏览了文档:s 你的链接抛出错误@Melvyn
  • 链接对我来说很好,但这里有另一个:djangopackages.org/packages/p/django-filter

标签: python sql django


【解决方案1】:

要动态执行查询,您将带有项目'fieldname__lookuptype': valuedict 作为**kwargs 传递给Model.objects.filter

所以要在上面的示例中过滤rankyear_beganyear_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)

关于ResearchMetrics 中的total_citationsh_index 字段,我假设您想要聚合这些值。因此,在上面的示例中,您需要总和或平均值。

原理是一样的:

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_FIELDSMETRICS_FIELDS 中的字段名称来确保的。

【讨论】:

    猜你喜欢
    • 2011-06-08
    • 2012-10-25
    • 2018-05-12
    • 1970-01-01
    • 2013-06-09
    • 2011-10-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多