Django 4.0 получение объектов поля "многие ко многим" в представлении списка
Я совсем новичок в Django и застрял в нем. У меня есть модель книги и модель жанра, которые имеют отношения "многие ко многим". Как мне получить все книги определенного жанра в виде списка. Я предполагаю, что быстрее получить объект Genre и получить все книги из него с помощью запроса типа "genre.objects.book_set.all", чем перебирать все книги и смотреть, есть ли в них указанный жанр. Однако я не уверен, как я могу сделать это в ListView? Я хочу взять название жанра из url и затем использовать его для получения объекта жанра. Вот что у меня есть
URL:
path('genre/<string:name>', GenreListView.as_view(), name='book-genre'),
Модели:
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
Вид?
class GenreListView(ListView):
model = Genre
def get_queryset(self):
Not sure what to put here....
return
Любая помощь будет принята с благодарностью, спасибо.
Вы можете сделать примерно следующее:
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 %}
Вы должны передать id
жанр, затем применить фильтр в методе get_queryset
, чтобы получить все книги для определенных жанров следующим образом:
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'])
Тогда в шаблоне можно показать книги, относящиеся к определенному жанру.
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>
Или вы можете просто сделать это с помощью представлений, основанных на функциях.
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})
Редактирование:
Если вы хотите отфильтровать все книги по названию Genre
, сделайте следующее:
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})
Я решил переключить представление на детальное представление вместо представления списка, и тогда с html-страницы я смогу получить доступ к книгам, связанным с этим жанром.
html:
{% for book in genre.book_set.all %}
<li>{{ book.name }}</li>
</a>
{% endfor %}
view:
class GenreDetailView(DetailView):
model = Genre
context_object_name = "genre"
модель:
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'),
Я считаю, что это лучше, чем просматривать каждую книгу и проверять, есть ли в ней жанр, который вы ищете.