Count all records on secondary table in ManyToMany relationship with Django

Question:

How would I count the total number of readers associated with each book in index.html? In index.html, the first loop iterates through readers and provides a sum for the number of books associated with each reader. The second loop does not count the number of books a reader has read (I understand why).

I’m trying to count the total number of readers associated with each book. Is there a way to complete this task within index.html? Or should this be done in views.py? Or should I adjust the relationship in models.py?

I’ve been googling but haven’t found an answer.

models.py

class Book(models.Model):
    title = models.CharField(max_length=100)

class Reader(models.Model):
    name = models.CharField(max_length=100)
    book_read = models.ManyToManyField(Book)

views.py

def index(request):
    all_readers = Reader.objects.all()
    all_books = Book.objects.all()

    context = {
        "all_readers": all_readers,
        "all_books": all_books,
    }
    return render(request, "testing/index.html", context)

index.html

{% for reader in all_readers %}
    <h3>{{ reader.book.all.count }}</h3>
{% endfor %}

{% for book in all_books %}
    <h3>{{ book.reader.all.count }}</h3>
{% endfor %}
Asked By: s_m_lima

||

Answers:

To count the total number of readers associated with each book in index.html, you can use the Count function from the Django ORM to get the number of readers for each book.

Here’s an example of how you can do this in your template:

{% for book in all_books %}
<h3>{{ book.reader_set.all.count }}</h3>
{% endfor %}

This will display the number of readers associated with each book.

Alternatively, you can also use the annotate method in the view function to add a num_readers field to each book object, which you can then display in the template using the {{ book.num_readers }} syntax:

views.py:

def index(request):
    all_readers = Reader.objects.all()
    all_books = Book.objects.all().annotate(num_readers=Count('reader'))
    context = {
        "all_readers": all_readers,
        "all_books": all_books,
    }
    return render(request, "testing/index.html", context)

index.html:

{% for book in all_books %}
<h3>{{ book.num_readers }}</h3>
{% endfor %}

This will also display the number of readers associated with each book.

It is generally recommended to do as much of the data processing as possible in the view function and pass the processed data to the template, rather than trying to do data processing in the template itself. This helps to keep the template code clean and easy to understand, and also makes it easier to debug any issues that may arise.

Answered By: Shams
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.