Method annotate()
specifies the calculated value for each object in the QuerySet.
For example, we have the following models:
- Author
- Post
Model Post
has a ForeignKey field called author
related to the model Author
. Each author can write multiple posts.
if you are retrieving a list of authors and want to know how many posts are written by each author. Here annotate()
method comes for help.
models.py
from django.db import models
class Author(models.Model):
name = models.CharField(max_length=200)
email = models.EmailField()
def __str__(self):
return self.name
class Post(models.Model):
title = models.CharField(max_length=256)
slug = models.SlugField(unique=True)
text = models.TextField()
author = models.ForeignKey(Author, on_delete=models.CASCADE)
def __str__(self):
return self.title
In the author_list
view we will render the authors list ordered by post count in descending order. When an annotate()
clause is specified, each author object in the QuerySet
will be annotated with the post count called num_posts
. Then we can order the queryset by num_posts
.
views.py
from django.shortcuts import render
from django.db.models import Count
from . import models
def author_list(request):
authors = models.Author.objects.all()
authors = authors.annotate(num_posts=Count('post')).order_by('-num_posts')
context = {
'authors': authors
}
return render(
request,
'blog/authors.html',
context=context
)
Our blog/authors.html
template looks like this:
<body>
<div class="container mt-5">
<table class="table">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Author</th>
<th scope="col">Post count:</th>
</tr>
</thead>
<tbody>
{% for author in authors %}
<tr>
<th scope="row">1</th>
<td>{{ author.name }}</td>
<td>{{ author.num_posts }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</body>
</html>
The screenshot of the authors page is shown below: