Представление для отображения записей по категориям
Переходим в файл views.py в нашем приложении blog, и добавим следующий код:
from django.views.generic import ListView, DetailView
from .models import Post, Category
class PostFromCategory(ListView):
template_name = 'blog/post_list.html'
context_object_name = 'posts'
category = None
def get_queryset(self):
self.category = Category.objects.get(slug=self.kwargs['slug'])
queryset = Post.objects.filter(category__slug=self.category.slug)
if not queryset:
sub_cat = Category.objects.filter(parent=self.category)
queryset = Post.objects.filter(category__in=sub_cat)
return queryset
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['title'] = f'Записи из категории: {self.category.title}'
return context
Давайте еще раз разберем работу, мы наследуемся от ListView класса, это представление будет обрабатывать наш список объектов:
model- название нашей модели,Post.template_name- По умолчаниюListViewищет шаблон с префиксом имени модели и суффиксом_list.html, если не установлено иное. Это можно переопределить, установив атрибутtemplate_name. Мы будем использовать тот же шаблон, что и в классеPostListView.context_object_name- Переопределим имя Queryset по умолчаниюobject_list, установив атрибутcontext_object_name. Это помогает иметь более удобное для работы имя. Мы будем использоватьposts, так как в шаблонеpost_list.htmlмы уже используем ее.category- переменная, по которой мы будем работатьdef get_queryset- метод обработки запросов, здесь мы получаем категорию по определенному slug, а после мы фильтруем запросы статей по категории и возвращаем QuerySet. Это работает только для дочерних категорий, если данный объект пустой(при переходе в родительскую категорию), то мы получаем все дочерние категории, и выводим все записи из них.get_context_data- в этом методе передаем<title></title>категории в наш шаблон.
Следующим шагом, нам необходимо добавить в urls.py маршруты вывода записей из категорий.
from django.urls import path
from .views import PostListView, PostDetailView, PostFromCategory
urlpatterns = [
path('', PostListView.as_view(), name='home'),
path('post/<str:slug>/', PostDetailView.as_view(), name='post_detail'),
path('category/<str:slug>/', PostFromCategory.as_view(), name="post_by_category"),
]
Теперь нам нужно ещё добавить метод в модель категорий Category:
class Category(MPTTModel):
# поля модели
def get_absolute_url(self):
"""
Получаем прямую ссылку на категорию
"""
return reverse('post_by_category', kwargs={'slug': self.slug})
def __str__(self):
"""
Возвращение заголовка статьи
"""
return self.title
Данный метод позволяет получать прямую ссылку на категорию, без вызова {% url '' %}.
Давайте откроем шаблон post_list.html и добавим туда ссылки на категории:
{% extends 'main.html' %}
{% block content %}
{% for post in posts %}
<div class="card mb-3">
<div class="row">
<div class="col-4">
<img src="{{ post.thumbnail.url }}" class="card-img-top" alt="{{ post.title }}">
</div>
<div class="col-8">
<div class="card-body">
<h5 class="card-title">
<a href="{{ post.get_absolute_url }}">{{ post.title }}</a>
</h5>
<p class="card-text">{{ post.description }}</p>
<small>Добавил {{ post.author.username }}, {{ post.create }},</small>
в категорию: <a href="{{ post.category.get_absolute_url }}">{{ post.category.title }}</a>
</div>
</div>
</div>
</div>
{% endfor %}
{% endblock %}
Мы использовали в модели построение ссылки через метод get_absolute_url.
Можно пользоваться методом, либо получать ссылку через <a href="{% url 'post_by_category' post.category.slug %}">{{ post.category.title }}</a>
Запустим сервер и перейдем на главную страницу блога:
Мы видим что у постов стали отображаться категории с активной ссылкой, также в сайдбаре ссылки на категории стали активны. Перейдем в одну из них:
Мы успешно сделали вывод записей по категориям, осталось добавить пагинацию к их выводу.