如何在 Django 数据库查询中查询列的元组?
Posted
技术标签:
【中文标题】如何在 Django 数据库查询中查询列的元组?【英文标题】:How to query tuples of columns in Django database queries? 【发布时间】:2020-07-08 17:49:10 【问题描述】:我有一些表 ports(switch_ip, slot_number, port_number, many, more, columns)
并希望使用 Django 实现以下 PostgreSQL 查询:
SELECT switch_ip, array_agg((slot_number, port_number, many, more, columns) ORDER BY slot_number, port_number) info
FROM ports
GROUP BY switch_ip
ORDER BY switch_ip
使用django.contrib.postgres.aggregates
这是我目前得到的结果:
Port.objects \
.values('switch_ip') \
.annotate(
info=ArrayAgg('slot_number', ordering=('slot_number', 'port_number'))
) \
.order_by('switch_ip')
我无法在ArrayAgg
中包含多于一列。 ArrayAgg(a, b, c)
、ArrayAgg((a, b, c))
、ArrayAgg([a, b, c])
似乎都不起作用。解决方法可能涉及为每列单独的ArrayAgg
s,并且每列都具有相同的顺序。我会鄙视这一点,因为我有很多专栏。有没有更好的解决方法,可能更底层?
我怀疑这不是 ArrayAgg 本身的问题,而是一般的元组表达式。有没有办法在 Django 查询中使用元组?例如,对应的 Django 是什么:
SELECT switch_ip, (slot_number, port_number, many, more, columns) info
FROM ports
如果这在 Django 中还不能实现,那么实现起来有多可行?
【问题讨论】:
只是为了理解你的问题,为什么要在一个元组中返回这些值,而不是仅仅在python中构建元组? @GrandPhuba 解决我最初的问题:不必使用多个info1=ArrayAgg(a, ordering=o), info2=ArrayAgg(b, ordering=o), info3=ArrayAgg(c, ordering=o)
进行注释,其中 o
每次都是相同的顺序。如果ArrayAgg
可以在元组上工作,那么它只是类似于info=ArrayAgg((a, b, c), ordering=o)
。因为我怀疑这不是 ArrayAgg
特有的问题,所以我将问题扩大到 Django 中的元组。
【参考方案1】:
做了更多的研究后,我想可以添加缺少的元组功能,如下所示:
-
创建一个名为
TupleField
的新模型字段类型。该实现可能看起来有点类似于django.contrib.postgres.fields.ArrayField
。 TupleField
会很尴尬,因为我认为任何 RDBMS 都不允许将复合类型用作列类型,因此 TupleField
的使用将仅限于(可能是中间?)查询结果。
创建一个新的django.db.models.Expression
子类,它自己包装多个表达式(通常类似于Func
,因此查看Func
的实现可能是值得的)并计算为TupleField
。例如,将此子类命名为 TupleExpression
。
然后我可以简单地用ArrayAgg(TupleExpression('slot_number', 'port_number', 'many', 'more', 'columns'), ordering=('slot_number', 'port_number'))
注释来解决我原来的问题。这将使用正确排序的元组数组注释每个switch_ip
,其中每个元组代表一个交换机端口。
【讨论】:
否决这一点,因为采用任何 NoSQL 数据库可能比使用 Django 内部实现像元组这样基本的东西更有效率和满足感。哦,等等,我不能。那为什么会显示向下的箭头呢?【参考方案2】:我花了很多时间寻找可行的解决方案,这里有一个完整的代码示例。
-
您需要在模板中用方括号定义数组“函数”
from django.db.models.expressions import Func
class Array(Func):
template = '%(function)s[%(expressions)s]'
function = 'ARRAY'
-
您需要定义输出字段格式(它必须是一些 django 字段的数组)。例如字符串数组
from django.contrib.postgres.fields import ArrayField
from django.db.models.fields import CharField
out_format = ArrayField(CharField(max_length=200))
-
最后制作一个 ArrayAgg 表达式
from django.db.models import F
annotate = '2-fields': ArrayAgg(Array(F('field1'), F('field2'), output_field=out_format), distinct=True)
model.objects.all().annotate(**annotate)
-
(可选)如果 field1 或 field2 不是 CharFields,您可以包含 Cast 作为 Array 的参数
from django.db.models.functions import Cast
annotate = '2-fields': ArrayAgg(Array(Cast(F('field1'), output_field=CharField(max_length=200)), F('field2'), output_field=out_format), distinct=True)
【讨论】:
以上是关于如何在 Django 数据库查询中查询列的元组?的主要内容,如果未能解决你的问题,请参考以下文章