动态计算聚合 Django 中的对象

Dynamically counting objects in an aggregate Django

我有一个相对复杂的查询要处理,我正在努力反对它。

请允许我解释一下场景。

我想 return 一个查询集,上面注解了 user 已完成的案例数量。

假设我的用户具有三个角色。 a_delegateb_certifiedc_accredited

我正在使用 Django 提供的内置 User 模型,没有什么特别之处。角色只是 Django 再次提供的内置 Group 模型的名称。

现在,我知道这可能会得到 "just write raw sql" 评论,这很好。我并没有排除在这里使用原始 sql 的可能性。我只是想知道是否有一种方法可以先使用 ORM 来完成此操作。

我有这个功能,它将输入映射到相关角色。

def convert_filter_str(str: str) -> Tuple:
    """
    Converts expected filters into course names and returns them
    and the relevant roles as a Tuple
    """

    APPLIANCES: Dict = {
        'a': 'Type A',
        'b': 'Type B',
        'c': 'Type C',
    }

    ROLES: Dict = {
        'a': ['a_certified', 'a_accredited'],
        'b': ['b_certified', 'b_accredited'],
        'c': ['c_certified', 'c_accredited'],
    }

    filters: List = str.split(',')
    converted_filters: List = []
    converted_roles: List = []

    for filter in filters:
        filter = filter.strip()
        converted_item = APPLIANCES[filter]
        converted_role = ROLES[filter]
        converted_filters.append(converted_item)
        converted_roles.append(converted_role)

    return converted_filters, converted_roles

因此,如果用户将过滤器输入为 a,b,则:

如果我们考虑我之前提到的内容,User 具有三个角色。 a_delegateb_certifiedc_accredited 所以根据上面的过滤器,我们应该只查看 return 对 Type B.[=41 的案例计数=]

为简洁起见,我已经有了一个包含该用户的查询集。

我需要根据用户的输入对其进行过滤,因此他们应用的过滤器越多,添加的计数就越多。

我想过使用 Sum,其中包含一个计数聚合列表,但是会抛出 django.db.utils.ProgrammingError: can't adapt type 'Count'

final_qs: User = user.annotate(
    completed_cases=(Sum(
        [Count(
            'patientcase',
            filter=Q(
                groups__name__in=role_filter[i]
            )
        ) for i in range(len(role_filter))],
        output_field=IntegerField()
    ))
)

我还考虑过使用 Sum,其中包含一个计数聚合生成器,但是会抛出 psycopg2.ProgrammingError: can't adapt type 'generator'

final_qs: User = user.annotate(
    completed_cases=(Sum(
        (Count(
            'patientcase',
            filter=Q(
                groups__name__in=role_filter[i]
            )
        ) for i in range(len(role_filter))),
        output_field=IntegerField()
    ))
)

有没有办法通过 ORM 来完成这项工作?

我的解决方案创建了一个表达式,然后可以将其传递给注释。

def build_filtered_count(appliance_filter, role_filter):
    """
    Dynamically builds count exprerssions based on the filters
    passed to the function
    """

    counts = [Count(
        'patientcase',
        filter=Q(
            groups__name__in=role_filter[i]
        ), distinct=True
    ) for i in range(len(role_filter))]

    return sum(counts)