动态计算聚合 Django 中的对象
Dynamically counting objects in an aggregate Django
我有一个相对复杂的查询要处理,我正在努力反对它。
请允许我解释一下场景。
我想 return 一个查询集,上面注解了 user
已完成的案例数量。
假设我的用户具有三个角色。 a_delegate
、b_certified
和 c_accredited
。
a_delegate
告诉我们 user
正在接受课程 a
的培训,
我们不想计算课程 a
的任何已完成案例。
b_certified
告诉我们 user
已完成课程 b
的培训并获得认证。我们需要统计这些已完成的案例
c_accredited
告诉我们 user
已经通过了课程 b
的培训。我们需要统计这些已完成的案例
我正在使用 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
,则:
converted_filters
应该 return ['Type A', 'Type B']
converted_roles
应该 return [['a_certified', 'a_accredited'], ['b_certified', 'b_accredited']]
如果我们考虑我之前提到的内容,User
具有三个角色。 a_delegate
、b_certified
和 c_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)
我有一个相对复杂的查询要处理,我正在努力反对它。
请允许我解释一下场景。
我想 return 一个查询集,上面注解了 user
已完成的案例数量。
假设我的用户具有三个角色。 a_delegate
、b_certified
和 c_accredited
。
a_delegate
告诉我们user
正在接受课程a
的培训, 我们不想计算课程a
的任何已完成案例。b_certified
告诉我们user
已完成课程b
的培训并获得认证。我们需要统计这些已完成的案例c_accredited
告诉我们user
已经通过了课程b
的培训。我们需要统计这些已完成的案例
我正在使用 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
,则:
converted_filters
应该 return['Type A', 'Type B']
converted_roles
应该 return[['a_certified', 'a_accredited'], ['b_certified', 'b_accredited']]
如果我们考虑我之前提到的内容,User
具有三个角色。 a_delegate
、b_certified
和 c_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)