Django queryset StringAgg на поле массива
У меня есть некоторые данные, которые включают размеры, очень похожие на модель ниже.
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-func. Но я не уверен, с чего начать. Как я могу этого добиться?
Оказалось, что проблема была двоякой.
- Очистка от значений Null может быть выполнена с помощью array_remove .
- Склеивание строк с разделителем через 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,
),
Value(" x "),
Value(''),
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