Django queryset StringAgg on arrayfield



我有一些包括尺寸的数据,就像下面的模型。

class Product(models.Model):
width = models.CharField()
height = models.CharField()
length = models.CharField()

通过注释,我们有一个名为at_size的字段,它产生如下数据:

  • [None, None, None]
  • ['200', '000', '210']
  • ['180', None, None]

这是这样完成的(感谢:)https://stackoverflow.com/a/70266320/5731101:

class Array(Func):
template = '%(function)s[%(expressions)s]'
function = 'ARRAY'
out_format = ArrayField(CharField(max_length=200))
annotated_qs = Product.objects.all().annotate(
at_size=Array(F('width'), F('height'), F('length'), 
output_field=out_format)
)

我想把这个转换成:

  • "
  • '200 x 000 x 210'
  • "180">

在代码中,这可能有点像' x '.join([i for i in data if i])。但是,由于我需要使用数据库函数来完成这一点,这有点更具挑战性。

我一直在玩StringAgg,但我一直得到:

HINT: No function matches the given name and argument types. You might need to add explicit type casts.

看起来我需要确保None值从初始的array - function中排除。但我不知道从哪里开始。我怎样才能做到这一点呢?

问题是双重的。

  1. 清除Null值可以通过使用array_remove
  2. 来完成
  3. 通过StringAgg用分隔符粘合字符串只有在输入是字符串时才有效。但由于我们使用的是数组,所以这不是正确的方法。而是使用array_to_string

最终结果如下:

class Array(Func):
# https://www.postgresql.org/docs/9.6/functions-array.html
template = '%(function)s[%(expressions)s]'
function = 'ARRAY'
class ArrayRemove(Func):
# https://www.postgresql.org/docs/9.6/functions-array.html
function = 'array_remove'
class ArrayToString(Func):
# https://stackoverflow.com/a/57873772/5731101
function = 'array_to_string'
out_format = ArrayField(CharField(max_length=200))
annotated_qs = annotated_qs.annotate(
at_size=ArrayToString(
ArrayRemove(
Array(F('width'), F('height'), F('length'), output_field=out_format),
None, # Remove None values from the Array with ArrayRemove                
),
Value(" x "),  # Delimiter.
Value(''),  # If there are null-values, replace with... (fallback)
output_field=CharField(max_length=200),
)
)

生成所需的格式:

for product in annotated_qs:
print(product.at_size)
180 x 000 x 200
180 x 026 x 200
180 x 7 x 200
180 x 000 x 200
200 x 000 x 220
180 x 000 x 200
175 x 230 x 033
160 x 000 x 200
60 x 220
Product.objects.annotate(display_format=Concat(F('width'), '×', F('height'), '×', F('length')))

应该做的伎俩不是吗?

不需要过于复杂,让我们保持它的漂亮和简单,并使用数据库连接3个字符串,并用乘法符号分开它们(显然,如果你喜欢另一个字符替换)。

看看这里的文档:https://docs.djangoproject.com/en/4.1/ref/models/database-functions/concat

最新更新