Reputation: 23
I'm trying to get a list of latest 100 posts and also the aggregated count of approved, pending, and rejected posts for the user of that post.
models.py
class BlogPost(models.Model):
POST_STATUSES = (
('A', 'Approved'),
('P', 'Pending'),
('R', 'Rejected')
)
author = models.ForeignKey(User)
title = models.CharField(max_length=50)
description = models.TextField()
status = models.ChoiceField(max_length=1, choices=POST_STATUSES)
views.py
Now I'm getting the the aggregated count like so, but I'm confused on how to merge the count with the title of the posts
top_post_users = list(BlogPost.objects.values_list('user_id', flat=True))[:100]
users = User.objects.filter(pk__in=top_post_users).annotate(approved_count=Count(Case(When(user_posts__status="A", then=1),output_field=IntegerField()))).annotate(pending_count=Count(Case(When(user_posts__status="P", then=1),output_field=IntegerField()))).annotate(reject_count=Count(Case(When(user_posts__status="R", then=1),output_field=IntegerField())))
users.values('approved_count', 'pending_count', 'reject_count')
This is the result I want:
How can I merge the returned counts with the titles?
I know I could use a for loop and append each one, but efficiency wise I don't think this is the right way to do it. Is there a more efficient way using django database ORM?
I've tried this
users.values('title', approved_count', 'pending_count', 'reject_count')
...and that works, but it returns more than the latest 100 posts, so I think it's getting all the posts for those users and the aggregated count.
Upvotes: 1
Views: 205
Reputation: 16661
Ultimately, you want a list of BlogPosts:
main_qs = BlogPost.objects
# add filters, ordering etc. of the posts
and you want to display not only the authors next to the title of the post but also enrich the author information with the annotated counts.
from django.db.models import OuterRef, Subquery, Count
# you need subqueries to annotate the blog posts
base_sub_qs = BlogPost.objects.filter(author__pk=OuterRef('author__pk'))
# add subqueries for each count
main_qs = main_qs.annotate(
user_approved_count=Subquery(base_sub_qs.filter(status="A").annotate(
c=Count('*')).values('c'), output_field=IntegerField()),
user_pending_count=Subquery(base_sub_qs.filter(status="P").annotate(
c=Count('*')).values('c'), output_field=IntegerField()),
user_rejected_count=Subquery(base_sub_qs.filter(status="R").annotate(
c=Count('*')).values('c'), output_field=IntegerField()),
)
You can then access these in your template:
{% for post in posts %}
{{ post.title }}
{{ post.author.get_full_name }}
approved: {{ post.user_approved_count }}
pending: {{ post.user_pending_count }}
rejected: {{ post.user_rejected_count }}
{% endfor %}
Documentation: https://docs.djangoproject.com/en/2.1/ref/models/expressions/#subquery-expressions
Upvotes: 2