Home > Enterprise >  Django 4.0 getting many to many field objects in List View
Django 4.0 getting many to many field objects in List View

Time:06-07

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:

urls.py:

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

Models.py:

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

Views.py

class GenreListView(ListView):

    model = Genre

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

        return 

CodePudding user response:

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 %}

CodePudding user response:

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})
  • Related