How to group a model data based on another model and paginate in django?

291 Views Asked by At

I have two models:

  1. Category - Zero or multiple books can be in one category
  2. Book - A book can have zero or one category

if I do Book.objects.all() I'll get something like [book11, book10, book9, ...] normally but I don't want that. What I want is something like:

[
 [book11, book2, book1],
 [book10],
 [book8, book6],
 [book7],
 [book4, book3],
 ...
]

Where

  • Books are grouped according to their category. The books that don't have a category will also include in the list as a single element group
  • Ordering would be according to book's creation in reverse

For better understanding here is my model structure:

class Category(models.Model):
    name = models.CharField(max_length=50)

class Book(models.Model):
    name = models.CharField(max_length=128)
    category = models.ForeignKey(Category, on_delete=models.CASCADE,related_name='books', null=True, blank=True)

1

There are 1 best solutions below

0
Iain Shelvington On

To do this grouping in Python code you can use itertools.groupby

from itertools import groupby


qs = Book.objects.order_by('category', '-created_at')
grouped_books = groupby(qs, lambda book: book.category)
for category, books in grouped_books:
    print(category)
    print(list(books))

You can also do this in a template using the regroup tag

In your view pass this queryset to your template context

   books = Book.objects.order_by('category', '-created_at')

In your template

{% regroup books by category as category_list %}

<ul>
{% for category in category_list %}
    <li>{{ category.grouper }}
    <ul>
        {% for book in category.list %}
          <li>{{ book }}</li>
        {% endfor %}
    </ul>
    </li>
{% endfor %}
</ul>