python - 动态计算聚合 Django 中的对象
问题描述
我有一个相对复杂的查询要处理,我正在努力解决它。
请允许我解释一下这个场景。
我想返回一个用案例数量注释的查询集user
已完成认证的案例数量进行注释。
想象一下我的用户有三个角色。a_delegate
,b_certified
和c_accredited
.
a_delegate
告诉我们user
正在为课程进行培训a
,我们不想为课程计算任何已完成的案例a
。b_certified
告诉我们user
已完成课程培训b
并获得认证。我们需要统计这些已完成的案例c_accredited
告诉我们,他们user
已经超越了他们的课程培训b
。我们需要统计这些已完成的案例
我使用的是 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
那么:
converted_filters
应该返回['Type A', 'Type B']
converted_roles
应该返回[['a_certified', 'a_accredited'], ['b_certified', 'b_accredited']]
如果我们考虑我之前提到的,User
具有三个角色。a_delegate
,b_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 完成这项工作?
解决方案
我提出的解决方案创建了一个表达式,然后可以将其传递给注释。
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)
推荐阅读
- python - 如何根据列唯一集将行保留在 DataFrame 中?
- typescript - 如何在 Typescript 中指定数字的最小-最大范围?
- css - 我无法以角度设置身体背景图像
- python - Tkinter 透明框架文本未显示
- javascript - 如何根据内容中的过滤器编号隐藏表格?
- c++ - ONNX 模型中的转发过程失败
- python - 哪些内置抽象基类支持切片?
- c# - AWS Firehose 转换 Lambda 函数为每条记录引发 Lambda.MissingRecordId 错误
- python-3.x - 如何将 html 文件转换为 pdf 文件?
- azure - Azure Graph Api 获取外部用户信息