Откройте шаблон blog/post/list.html и добавьте в него следующий ниже исходный код HTML:
{% extends "blog/base.html" %}
{% block title %}My Blog{% endblock %}
{% block content %}
<h1>My Blog</h1>
{% for post in posts %}
<h2>
<a href="{{ post.get_absolute_url }}">
{{ post.title }}
</a>
</h2>
<p class="tags">Tags: {{ post.tags.all|join:", " }}</p>
<p class="date">
Published {{ post.publish }} by {{ post.author }}
</p>
{{ post.body|truncatewords:30|linebreaks }}
{% endfor %}
{% include "pagination.html" with page=page_obj %}
{% endblock %}
Шаблонный фильтр join работает так же, как метод Python string.join(), чтобы конкатенировать элементы с заданной строкой.
Пройдите по URL-адресу http://127.0.0.1:8000/blog/ в своем браузере. Под заголовком каждого поста вы должны увидеть список тегов:
Далее мы отредактируем представление post_list, чтобы пользователи имели возможность отображать список всех постов, помеченных конкретным тегом.
Откройте views.py файл приложения blog, импортируйте модель Tag из приложения django-taggit и измените представление post_list, как показано ниже, чтобы при необходимости фильтровать посты по тегу:
from taggit.models import Tag
def post_list(request, tag_slug=None):
post_list = Post.published.all()
tag = None
if tag_slug:
tag = get_object_or_404(Tag, slug=tag_slug)
post_list = post_list.filter(tags__in=[tag])
# Постраничная разбивка с 3 постами на страницу
paginator = Paginator(post_list, 3)
page_number = request.GET.get('page', 1)
try:
posts = paginator.page(page_number)
except PageNotAnInteger:
# Если page_number не целое число, то
# выдать первую страницу
posts = paginator.page(1)
except EmptyPage:
# Если page_number находится вне диапазона, то
# выдать последнюю страницу результатов
posts = paginator.page(paginator.num_pages)
return render(request,
'blog/post/list.html',
{'posts': posts,
'tag': tag})
Теперь представление post_list работает следующим образом:
-
Представление принимает опциональный параметр
tag_slug, значение которого по умолчанию равноNone. Этот параметр будет передан в URL-адресе. -
Внутри указанного представления формируется изначальный набор запросов, извлекающий все опубликованные посты, и если имеется слаг данного тега, то берется объект
Tagс данным слагом, используя функцию сокращенного доступаget_object_or_404(). -
Затем список постов фильтруется по постам, которые содержат данный тег. Поскольку здесь используется взаимосвязь многие-ко-многим, необходимо фильтровать записи по тегам, содержащимся в заданном списке, который в данном случае содержит только один элемент. Здесь используется операция
__inпоиска по полю. Взаимосвязи многие-ко-многим возникают, когда несколько объектов модели ассоциированы с несколькими объектами другой модели. В нашем приложении пост может иметь несколько тегов, и тег может быть связан с несколькими постами. -
Наконец, теперь функция
render()передает новую переменнуюtagв шаблон.
Напомним, что итерируемые наборы запросов QuerySet являются ленивыми. Наборы запросов, служащие для извлечения постов, будут оцениваться только при прокручивании списка постов в цикле во время прорисовки шаблона.
Откройте файл urls.py приложения blog, закомментируйте основанный на классе шаблон URL-адреса PostListView и раскомментируйте представление post_list, как показано ниже:
path('', views.post_list, name='post_list'),
# path('', views.PostListView.as_view(), name='post_list'),
Добавьте следующий ниже дополнительный шаблон URL-адреса, чтобы отображать список постов по тегу:
path('tag/<slug:tag_slug>/',
views.post_list, name='post_list_by_tag'),
Как вы видите, оба шаблона указывают на одно и то же представление, но у них разные имена.
Первый шаблон будет вызывать представление post_list без каких-либо опциональных параметров,
тогда как второй шаблон будет вызывать это представление с параметром tag_slug.
Конвертер путей slug используется для сочетания с параметром, представленным строковым литералом в нижнем регистре с буквами или цифрами ASCII, а также символами дефиса и подчеркивания.
Теперь файл urls.py приложения blog должен выглядеть следующим образом:
from django.urls import path
from . import views
app_name = 'blog'
urlpatterns = [
# представления поста
path('', views.post_list, name='post_list'),
# path('', views.PostListView.as_view(), name='post_list'),
path('tag/<slug:tag_slug>/',
views.post_list, name='post_list_by_tag'),
path('<int:year>/<int:month>/<int:day>/<slug:post>/',
views.post_detail,
name='post_detail'),
path('<int:post_id>/share/',
views.post_share, name='post_share'),
path('<int:post_id>/comment/',
views.post_comment, name='post_comment'),
]
Поскольку используется представление post_list, отредактируйте шаблон blog/post/list.html, видоизменив постраничную разбивку, чтобы использовать объект posts:
{% include "pagination.html" with page=posts %}
Добавьте в шаблон blog/post/list.html следующие ниже строки:
{% extends "blog/base.html" %}
{% block title %}My Blog{% endblock %}
{% block content %}
<h1>My Blog</h1>
{% if tag %}
<h2>Posts tagged with "{{ tag.name }}"</h2>
{% endif %}
{% for post in posts %}
<h2>
<a href="{{ post.get_absolute_url }}">
{{ post.title }}
</a>
</h2>
<p class="tags">Tags: {{ post.tags.all|join:", " }}</p>
<p class="date">
Published {{ post.publish }} by {{ post.author }}
</p>
{{ post.body|truncatewords:30|linebreaks }}
{% endfor %}
{% include "pagination.html" with page=posts %}
{% endblock %}
Если пользователь зайдет в блог, то он увидит список всех постов. Если он будет фильтровать по постам, помеченным конкретным тегом, то увидит тег, по которому он фильтрует.
Теперь отредактируйте шаблон blog/post/list.html, изменив вид отображения тегов, как показано ниже:
{% extends "blog/base.html" %}
{% block title %}My Blog{% endblock %}
{% block content %}
<h1>My Blog</h1>
{% if tag %}
<h2>Posts tagged with "{{ tag.name }}"</h2>
{% endif %}
{% for post in posts %}
<h2>
<a href="{{ post.get_absolute_url }}">
{{ post.title }}
</a>
</h2>
<p class="tags">
Tags:
{% for tag in post.tags.all %}
<a href="{% url 'blog:post_list_by_tag' tag.slug %}">
{{ tag.name }}
</a>
{% if not forloop.last %}, {% endif %}
{% endfor %}
</p>
<p class="date">
Published {{ post.publish }} by {{ post.author }}
</p>
{{ post.body|truncatewords:30|linebreaks }}
{% endfor %}
{% include "pagination.html" with page=posts %}
{% endblock %}
В приведенном выше исходном коде прокручиваются в цикле все теги поста, отображающие конкретно-прикладную ссылку на URL-адрес, чтобы фильтровать посты по этому тегу.
URL-адрес формируется с помощью тега {% url 'blog:post_list_by_tag' tag.slug %}, используя имя URL-адреса и тег slug в качестве его параметра. Теги отделяются запятыми.
Пройдите по URL-адресу http://127.0.0.1:8000/blog/tag/jazz/ в своем браузере, и вы увидите список постов, отфильтрованных по этому тегу, примерно как показано ниже: