如何从注释中将结果收集到数组中?

How to collect results into array from annotation?

是否可以从不同的 Case 指令收集结果并将它们存储在数组字段中?

几个测试实例:

banana = Fruit.objects.create(name='banana', type='tropicals', country_of_import='Africa')
orange = Fruit.objects.create(name='orange', type='citrus', country_of_import='Spain')
apple = Fruit.objects.create(name='apple', type='apples', country_of_import='Russia')

这位经理需要收集案例的所有结果。 ToArray 作为完成这项工作的可能功能。根据 docs:

有一个名称相似的函数 ArrayAgg

Returns a list of values, including nulls, concatenated into an array.

它适用于一个表达式,但不适用于多个表达式:

class CustomManager(Manager):

    def get_queryset(self):
        query = super().get_queryset().annotate(
            additional_info=ToArray(
                Case(
                    When(
                        type='tropicals',
                        then=Value('This fruit is tropical...')
                    ),
                    output_field=CharField()
                ),
                Case(
                    When(
                        country_of_import='Spain',
                        then=Value('This fruits was grown in Spain'),
                    ),
                    output_field=CharField()
                )
            )
        )
        return query

最后的结果是:

banana.additional_info = ['This fruit is tropical.']
orange.additional_info = ['This fruits was grown in Spain.']

这可以用现有的函数来处理还是我需要自己写一个?

实际上,没有明显的方法来实现它。最简单的解决方案是像这样使用 CombinedExpression class:

from django.contrib.postgres.aggregates import ArrayAgg
from django.db.models import Manager, Case, When, Value, CharField

from django.db.models.expressions import CombinedExpression

class FruitManager(Manager):
    def get_queryset(self):
        query = super().get_queryset()

        query = query.annotate(
            result=(
                CombinedExpression(
                    ArrayAgg(Case(
                        When(
                            type='tropicals',
                            then=Value('This fruit is tropical...'),

                        ),
                        output_field=CharField()
                    )),
                    '||'
                    ,
                    ArrayAgg(Case(
                        When(
                            country_of_import='Africa',
                            then=Value('This fruit is citrus...'),

                        ),
                        output_field=CharField(),
                        default=Value('default value')),
                    ),
                )
            )
        )
        return query

那将编译成下面的SQL:

SELECT "core_fruit"."id",
       (
               ARRAY_AGG(CASE
                             WHEN "core_fruit"."type" = 'tropicals'
                                 THEN 'This fruit is tropical...'
                             ELSE NULL END) ||
               ARRAY_AGG(
                       CASE
                           WHEN "core_fruit"."country_of_import" = 'Africa'
                               THEN 'This fruit is citrus...'
                           ELSE 'default value' END
                   )
           ) AS "result"
FROM "core_fruit"
GROUP BY "core_fruit"."id";

但有一件事值得注意,CombinedExpression 没有记录,因为仅供内部使用。