【发布时间】:2021-11-15 23:11:43
【问题描述】:
上下文
我对 Django 很陌生,我正在尝试编写一个复杂的查询,我认为在原始 SQL 中很容易写入,但我正在努力使用 ORM。
型号
我有几个名为SignalValue、SignalCategory、SignalSubcategory、SignalType、SignalSubtype 的模型具有与以下模型相同的结构:
class MyModel(models.Model):
id = models.BigAutoField(primary_key=True)
name = models.CharField()
fullname = models.CharField()
我还有表示模型 SignalValue 与其他模型 SignalCategory、SignalSubcategory、SignalType、SignalSubtype 之间关系的显式模型。这些关系中的每一个分别命名为SignalValueCategory、SignalValueSubcategory、SignalValueType、SignalValueSubtype。下面以SignalValueCategory 模型为例:
class SignalValueCategory(models.Model):
signal_value = models.OneToOneField(SignalValue)
signal_category = models.ForeignKey(SignalCategory)
最后,我还有以下两个模型。 ResultSignal存储了与模型相关的所有信号Result:
class Result(models.Model):
pass
class ResultSignal(models.Model):
id = models.BigAutoField(primary_key=True)
result = models.ForeignKey(
Result
)
signal_value = models.ForeignKey(
SignalValue
)
查询
我想要达到的目标如下。
对于给定的Result,我想检索属于它的所有ResultSignal,过滤它们以保留我感兴趣的内容,并用两个字段注释它们,我们将称之为filter_group_id 和filter_group_name。两个字段的值由给定的ResultSignal 的SignalValue 确定。
在我看来,实现这一点的最简单方法是首先用它们对应的filter_group_name 和filter_group_id 注释SignalValues,然后将生成的QuerySet 与ResultSignals 连接起来。但是,我认为在 Django 中不可能将两个QuerySets 连接在一起。因此,我认为我们可以使用Prefetch 对象来实现我想要做的事情,但似乎我无法使其正常工作。
代码
我现在将描述我的查询的当前状态。
首先,用它们对应的filter_group_name 和filter_group_id 注释SignalValues。请注意,以下代码中的filter_aggregator 只是一个复杂的过滤器,它只允许我选择想要的SignalValues。 group_filter 是同一个过滤器,但作为子过滤器列表。此外,filter_name_case 是一个条件表达式(Case() 构造):
# Attribute a group_filter_id and group_filter_name for each signal
signal_filters = SignalValue.objects.filter(
filter_aggregator
).annotate(
filter_group_id=Window(
expression=DenseRank(),
order_by=group_filters
),
filter_group_name=filter_name_case
)
然后,尝试加入/注释SignalResults:
prefetch_object = Prefetch(
lookup="signal_value",
queryset=signal_filters,
to_attr="test"
)
result_signals: QuerySet = (
last_interview_result
.resultsignal_set
.filter(signal_value__in=signal_values_of_interest)
.select_related(
'signal_value__signalvaluecategory__signal_category',
'signal_value__signalvaluesubcategory__signal_subcategory',
'signal_value__signalvaluetype__signal_type',
'signal_value__signalvaluesubtype__signal_subtype',
)
.prefetch_related(
prefetch_object
)
.values(
"signal_value",
"test",
category=F('signal_value__signalvaluecategory__signal_category__name'),
subcategory=F('signal_value__signalvaluesubcategory__signal_subcategory__name'),
type=F('signal_value__signalvaluetype__signal_type__name'),
subtype=F('signal_value__signalvaluesubtype__signal_subtype__name'),
)
)
通常,据我了解,生成的QuerySet 应该有一个现在可用的字段“test”,它将包含signal_filter 的字段,第一个QuerySet。但是,Django 抱怨在我的代码的最后一部分调用.values(...) 时找不到"test":Cannot resolve keyword 'test' into field. Choices are: [...]。就好像Prefetch对象的to_attr参数根本没有考虑进去。
问题
- 我是否误解了
annotate()和prefetch_related()函数的功能?如果不是,我在代码中做错了什么,指定参数to_attr不存在于我的结果QuerySet中? - 有没有更好的方法在 Django 中加入两个
QuerySets 或者我最好使用 RawSQL?另一种方法是切换到 Pandas 以在内存中进行连接,但通过精心设计的查询在 SQL 端进行此类转换通常更有效。
【问题讨论】:
标签: python django django-models django-queryset django-annotate