Reputation: 25
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
Upvotes: 1
Views: 567
Reputation: 25
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.
Upvotes: 0
Reputation: 8837
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})
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})
Upvotes: 2
Reputation: 1500
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 %}
Upvotes: 1