I have somewhat complex django query with a lot of .annotate:
query = ModuleEngagement.objects.filter(course_id=course_id)\
.values('username')\
.annotate(
videos_overall=Sum(Case(When(entity_type='video', then='count'), output_field=IntegerField()))) \
.annotate(
videos_last_week=Sum(Case(When(entity_type='video', created__gt=seven_days_ago, then=1),
output_field=IntegerField()))) \
.annotate(
problems_overall=Sum(Case(When(entity_type='problem', then='count'), output_field=IntegerField()))) \
.annotate(
problems_last_week=Sum(Case(When(entity_type='problem', created__gt=seven_days_ago, then='count'),
output_field=IntegerField()))) \
.annotate(
correct_problems_overall=Sum(Case(When(entity_type='problem', event='completed', then='count'),
output_field=IntegerField()))) \
.annotate(
correct_problems_last_week=Sum(Case(When(entity_type='problem', event='completed',
created__gt=seven_days_ago, then='count'),
output_field=IntegerField()))) \
.annotate(
problems_attempts_overall=Sum(Case(When(entity_type='problem', event='attempted', then='count'),
output_field=IntegerField()))) \
.annotate(
problems_attempts_last_week=Sum(Case(When(entity_type='problem', event='attempted',
created__gt=seven_days_ago, then='count'),
output_field=IntegerField()))) \
.annotate(
forum_posts_overall=Sum(Case(When(entity_type='discussion', then='count'),
output_field=IntegerField()))) \
.annotate(
forum_posts_last_week=Sum(Case(When(entity_type='discussion', created__gt=seven_days_ago, then='count'),
output_field=IntegerField()))) \
.annotate(
date_last_active=Max('created'))
Does annotate accept a dictionary as a parameter so I can move all the annotates into it? If so, what would be the syntax?
Does annotate accept a dictionary as a parameter so I can move all the annotates into it? If so, what would be the syntax?
You can perform dictionary unpacking. So if you have a dictionary like:
my_dict = {
'total_likes': Sum('likes'),
'total_articles': Sum('articles'),
}
The you can have a query like:
MyModel.objects.annotate(**my_dict)
This is then equivalent to:
MyModel.objects.annotate(total_likes=Sum('likes'), total_articles=Sum('articles'))
If you use django-2.0 or more recent, then you can significantly simplify your annotations with:
ModuleEngagement.objects.filter(course_id=course_id).values('username').annotate(
videos_overall=Count('pk', filter=Q(entity_type='video')),
videos_last_week=Count('pk', filter=Q(entity_type='video', created__gt=seven_days_ago)),
problems_overall=Count('pk', filter=Q(entity_type='problem'),
problems_last_week=Count('pk', filter=Q(entity_type='problem', created__gt=seven_days_ago)),
correct_problems_overall=Count('pk', filter=Q(entity_type='problem', event='completed'),
correct_problems_last_week=Count('pk', filter=Q(entity_type='problem', event='completed', created__gt=seven_days_ago)),
problems_attempts_overall=Count('pk', filter=Q(entity_type='problem', event='attempted'),
problems_attempts_last_week=Count('pk', filter=Q(entity_type='problem', event='attempted', created__gt=seven_days_ago)),
forum_posts__overall=Count('pk', filter=Q(entity_type='discussion'),
forum_posts__last_week=Count('pk', filter=Q(entity_type='discussion', event='attempted', created__gt=seven_days_ago)),
date_last_active=Max('created')
).order_by('username')