Django 5 для начинающих

Прогресс по курсу:  9/1004

11.3 Добавление функциональности тегирования
2 из 2 шагов пройдено

Создание представления для статей по тегу

Теперь мы создадим представление для фильтрации по тегу, для этого в файл views.py нашего приложения blog добавим следующий код:

from taggit.models import Tag

class PostByTagListView(ListView):
    model = Post
    template_name = 'blog/post_list.html'
    context_object_name = 'posts'
    paginate_by = 10
    tag = None

    def get_queryset(self):
        self.tag = Tag.objects.get(slug=self.kwargs['tag'])
        queryset = Post.objects.filter(tags__slug=self.tag.slug)
        return queryset

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['title'] = f'Статьи по тегу: {self.tag.name}'
        return context

Теперь можно перейти к обработке маршрутов и к шаблону.

 

Обработка представления в urls.py

Далее нам необходимо добавить обработку представления в urls.py приложения blog, добавим для этого следующую строку:

from django.urls import path
from .views import PostListView, PostDetailView, PostFromCategory, PostCreateView, PostUpdateView, CommentCreateView, PostByTagListView

urlpatterns = [
    path('', PostListView.as_view(), name='home'),
    path('post/create/', PostCreateView.as_view(), name='post_create'),
    path('post/<str:slug>/update/', PostUpdateView.as_view(), name='post_update'),
    path('post/<str:slug>/', PostDetailView.as_view(), name='post_detail'),
    path('post/<int:pk>/comments/create/', CommentCreateView.as_view(), name='comment_create_view'),
    path('post/tags/<str:tag>/', PostByTagListView.as_view(), name='post_by_tags'),  # New
    path('category/<str:slug>/', PostFromCategory.as_view(), name="post_by_category"),
]

Добавление тегов в шаблон

Откроем наш файл шаблона полной статьи templates/blog/post_detail.html, и отредактируем код, добавим блок card-footer:

{% extends 'main.html' %}
{% load mptt_tags %}
{% load static %}
{% block content %}
<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>{{ post.title }}</h5>
                <p class="card-text">{{ post.description }}</p>
				<p class="card-text">{{ post.text }}</p>
				Категория: <a href="{% url 'post_by_category' post.category.slug %}">{{ post.category.title }}</a> / Добавил: {{ post.author.username }} / <small>{{ post.time_create }}</small>
			</div>
		</div>
	</div>
	{% if post.tags.all %}
	<div class="card-footer border-0">
		Теги записи: {% for tag in post.tags.all %} <a href="{% url 'post_by_tags' tag.slug %}">{{ tag }}</a>, {% endfor %}
	</div>
	{% endif %}
</div>
<div class="card border-0">
	<div class="card-body">
		<h5 class="card-title">
			Комментарии
		</h5>
		{% include 'blog/comments/comments_list.html' %}
	</div>
</div>
{% endblock %}

В коде выше мы сначала проверяем наличие тегов у записи,  и далее выводим их в цикле.


Давайте проверим работу тегов на сайте. Запустим сервер и первым делом добавим теги для нескольких записей:

 

Теперь перейдем в просмотр записей:

И нажав на тег, мы перейдем к странице с просмотром записей с таким же тегом.

На этом разработка функциональности тегирования закончена, в следующих разделах мы продолжим дорабатывать наш проект.


  • Комментария
Будьте вежливы и соблюдайте наши принципы сообщества. Пожалуйста, не оставляйте решения и подсказки в комментариях, для этого есть отдельный форум.
Оставить комментарий

Подскажите, почему здесь

    path('post/tags/<str:tag>/', PostByTagListView.as_view(), name='post_by_tags'),  # New
    path('category/<str:slug>/', PostFromCategory.as_view(), name="post_by_category"),

str, а не slug?

На категории не обратил внимания изначально, а сейчас на тегах увидел

@ilya_kutaev, да, лучше изменить спецификаторы на slug.

Исправил, спасибо за замечание.

@Дмитрий_Селезнев, А такой url будет обработан таким шаблоном?

Я вижу, вы не стали заморачиваться генерацией слагов (для тегов) без кириллицы, хотя это возможно, а будет ли такой адрес распознан шаблоном <slug:>?

Дополнение: проверил, шаблон <slug:..> для тега, содержащего кириллицу, не срабатывает, слаг не может содержать. Именно поэтому и используется  шаблон <str:...>

Reverse for 'post_by_tags' with arguments '('программирование',)' not found. 1 pattern(s) tried: ['post/tags/(?P<tag>[-a-zA-Z0-9_]+)/\\Z']

Извините, что вас попутал, нужно вернуть str, иначе и категории, и теги с кириллицей будут работать некорректно

Для тегов сеть выход - в предыдущем уроке привел в комментарии.
Для категорий я использовал AutoSlugField из Django Extensions, описывал в предыдущих уроках, там тоже есть простое решение с определением метода slugify_function() у модели.
Поэтому у меня все теги имеют "правильные" слаги, и можно использовать <slug:> в шаблонах

Изменен ilya kutaev

@ilya_kutaev, с кириллицей спецификатор slug не сработает, по причине что кириллица будет преобразована в url-кодировку, а slug не пропустит символ %.

@ilya_kutaev, вернул обратно))

@Дмитрий_Селезнев, да, либо упадет при отображении статьи с кириллицей в теге - не будет найдет url шаблон, я выше привел именно эту ошибку.

Короче, нужно или str, или транслитерировать слаги - мне второй вариант нравится больше

Почему-то возникала ошибка AttributeError: 'TaggableManager' object has no attribute 'get_reverse_joining_fields', пока не обновил taggit до актуальной версии.
А еще зачем сначала all а потом filter, просто filter даст другой результат?

queryset = Post.objects.all().filter(tags__slug=self.tag.slug)

@Степан, ошибка эта, потому что используете Django 5, а курс пока еще по 4 версии. Скоро переведем его на 5.

Можете установить самый последний taggit, и это решит проблему.

pip install django-taggit

А по коду спасибо, исправил ошибку. Метод all() там лишний.