首页 > 解决方案 > 动态计算聚合 Django 中的对象

问题描述

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

请允许我解释一下这个场景。

我想返回一个用案例数量注释的查询集user已完成认证的案例数量进行注释。

想象一下我的用户有三个角色。a_delegate,b_certifiedc_accredited.

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

现在,我知道这很可能会得到“只写原始 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_certified因此c_accredited根据上面的过滤器,我们应该只考虑返回案例的计数Type B

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

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

我想过使用 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 完成这项工作?

标签: pythondjangopython-3.xdjango-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)

推荐阅读