Django.fun

Django 4.0 getting many to many field objects in List View

I'm really new to Django and have been stuck....I have a book model and a genre model that have a many to many relationship. How would I go about getting all the books for a specific genre in a list view. I'm assuming its faster to get a Genre object and get all the books from it using a query such as "genre.objects.book_set.all" rather than going through all books and seeing if they have the specified genre. However I'm not sure how I would do that in a ListView? I wanna take the genre name from the url and then use that to get the genre object. here is what I have

URL:

path('genre/<string:name>', GenreListView.as_view(), name='book-genre'),

Models:

class Genre(models.Model):
name = models.CharField(max_length=50, unique=True)
def __str__(self):
    return self.name


class Book(models.Model):
name = models.CharField(max_length=150, unique=True)
description = models.TextField()
user_count = models.IntegerField()
pages = models.IntegerField()
genres = models.ManyToManyField(Genre)
image = models.ImageField(default='book_imgs/default.jpg', upload_to='book_imgs')

def __str__(self):
    return self.name

View?

class GenreListView(ListView):

    model = Genre

    def get_queryset(self):
Not sure what to put here....

        return 

Any help is appreciated, thanks.

You can do something like this:

class SciFiListView(View):
    scifi_books = Book.objects.filter(genres__name__icontains="scifi")
    return render(request, "books/list.html", {"scifi_books": scifi_books})

list.html
{% for book in scifi_books %}
<p>{{ book.name }} | {{ book.description }}</p>
{% endfor %}

You should pass id of Genre, then apply filter in get_queryset method to get all the books for specific genres in the following way:

urls.py

path('genre/<int:id>/', GenreListView.as_view(), name='book_genre'),

views.py

class GenreListView(ListView):
    model = Book
    template_name = 'any_folder_name/any_file_name.html'
    context_object_name = 'specific_books'

    def get_queryset(self):
        qs = super().get_queryset()
        return qs.filter(genres=self.kwargs['id'])

Then, you can show books related to specific genre in the template.

any_file_name.html

<body>
    {% for book in specific_books %}
    <div>
        <p>{{book.name}}</p>
        <p>{{book.description}}</p>
        <p>{{book.pages}}</p>

    </div>
    <br>
    {% endfor %}
</body>

Or, you can simply do it with function based views too.

urls.py

 path('genre/<int:id>/', views.specific_books, name='specific_books'),

views.py

def specific_books(request, id):
    specific_books = Book.objects.filter(genres=id)
    return render(request, 'any_folder_name/any_file_name.html', {'specific_books': specific_books})

Edit:

If you want to filter all books through name of Genre, so do this:

urls.py

 path('genre/<str:name>/', views.specific_books, name='specific_books'),

views.py

def specific_books(request, name):
    specific_books = Book.objects.filter(genres__name__icontains=name)
    return render(request, 'home/index.html', {'specific_books': specific_books})

I decided to switch my view to a Detail View instead of a List View and then from the html page, I could access the books associated with that genre.

html:

{% for book in genre.book_set.all %}
        <li>{{ book.name }}</li>
    </a>
    {% endfor %}

view:

class GenreDetailView(DetailView):
    model = Genre
    context_object_name = "genre"

model:

class Genre(models.Model):
    name = models.CharField(max_length=50, unique=True)
    slug = models.SlugField(null=True, unique=True)

def __str__(self):
    return self.name

def get_absolute_url(self):
    return reverse("book-genre", kwargs={"slug": self.slug})

def save(self, *args, **kwargs): 
    if not self.slug:
        self.slug = slugify(self.name)
    return super().save(*args, **kwargs)

Url:

path('genre/<slug:slug>', GenreDetailView.as_view(), name='book-genre'),

I find this better than filtering through every single book and checking if it has the genre you are looking for.

Tutorials

Современный Python: начинаем проект с pyenv и poetry

Настройка проекта Python — виртуальные среды и управление пакетами

Использование requests в Python — тайм-ауты, повторы, хуки

Понимание декораторов в Python

ProcessPoolExecutor в Python: полное руководство

map() против submit() с ProcessPoolExecutor в Python

Понимание атрибутов, словарей и слотов в Python

Полное руководство по slice в Python

Выпуск Django 4.0

Безопасное развертывание приложения Django с помощью Gunicorn, Nginx и HTTPS

Автоматический повтор невыполненных задач Celery

Django REST Framework и Elasticsearch

Докеризация Django с помощью Postgres, Gunicorn и Nginx

Асинхронные задачи с Django и Celery

Релизы безопасности Django: 3.2.4, 3.1.12 и 2.2.24

Выпуски исправлений ошибок Django: 3.2.3, 3.1.11 и 2.2.23

Эффективное использование сериализаторов Django REST Framework

Выпуски безопасности Django: 3.2.2, 3.1.10 и 2.2.22

Выпущенные релизы безопасности Django: 3.2.1, 3.1.9 и 2.2.21

Обработка периодических задач в Django с помощью Celery и Docker

View all tutorials →