Вывод дерева категорий, пагинация, добавление Bootstrap 5
Меню категорий
Дерево категорий должно быть в боковой панели на каждой странице. Сделаем тег-включение.
modules/blog/templatetags/blog_tags.py (не забудьте __init__.py в папке templatetags):
from django import template
from modules.blog.models import Category
register = template.Library()
@register.inclusion_tag("includes/categories_menu.html")
def popular_categories():
return {"categories": Category.objects.all()}
Тег recursetree
django-mptt даёт тег, который рекурсивно обходит дерево:
templates/includes/categories_menu.html:
{% load mptt_tags %}
<div class="card mb-4">
<div class="card-header">Категории</div>
<ul class="list-group list-group-flush">
{% recursetree categories %}
<li class="list-group-item">
<a href="{{ node.get_absolute_url }}">{{ node.title }}</a>
{% if not node.is_leaf_node %}
<ul class="list-unstyled ms-3">{{ children }}</ul>
{% endif %}
</li>
{% endrecursetree %}
</ul>
</div>
node— текущая категория;{{ children }}— рекурсивно отрисованные дочерние узлы;- дерево строится из одного запроса благодаря полям
lft/rght/level.
Альтернатива — тег {% full_tree_for_model blog.Category as categories %} и фильтр tree_info.
Пагинация
В ListView — один атрибут:
class ArticleListView(ListView):
...
paginate_by = 4
Фрагмент templates/includes/pagination.html:
{% if is_paginated %}
<nav>
<ul class="pagination">
{% if page_obj.has_previous %}
<li class="page-item"><a class="page-link" href="?page=1">Первая</a></li>
<li class="page-item"><a class="page-link" href="?page={{ page_obj.previous_page_number }}">Предыдущая</a></li>
{% endif %}
{% for num in page_obj.paginator.page_range %}
{% if num == page_obj.number %}
<li class="page-item active"><span class="page-link">{{ num }}</span></li>
{% elif num > page_obj.number|add:"-3" and num < page_obj.number|add:"3" %}
<li class="page-item"><a class="page-link" href="?page={{ num }}">{{ num }}</a></li>
{% endif %}
{% endfor %}
{% if page_obj.has_next %}
<li class="page-item"><a class="page-link" href="?page={{ page_obj.next_page_number }}">Следующая</a></li>
<li class="page-item"><a class="page-link" href="?page={{ page_obj.paginator.num_pages }}">Последняя</a></li>
{% endif %}
</ul>
</nav>
{% endif %}
is_paginated— есть ли больше одной страницы;- выводим только ближайшие номера (±2 от текущей) — с фильтром
add.
В articles_list.html после цикла: {% include "includes/pagination.html" %}.
Bootstrap 5
Скачайте Bootstrap и положите в templates/src/ (это STATICFILES_DIRS) или подключите с CDN. templates/main.html:
{% load static blog_tags %}
<!doctype html>
<html lang="ru">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{ title }}</title>
<link rel="stylesheet" href="{% static 'bootstrap/css/bootstrap.min.css' %}">
<link rel="stylesheet" href="{% static 'custom/css/style.css' %}">
</head>
<body>
{% include "header.html" %}
<div class="container my-4">
<div class="row">
<div class="col-md-8">
{% block content %}{% endblock %}
</div>
<div class="col-md-4">
{% popular_categories %}
</div>
</div>
</div>
{% include "footer.html" %}
<script src="{% static 'bootstrap/js/bootstrap.bundle.min.js' %}"></script>
</body>
</html>
templates/header.html — navbar с логотипом, ссылкой на главную и (позже) кнопками входа и профиля.
Итог
- Меню категорий — тег-включение +
recursetreeизmptt_tags. - Пагинация в
ListView—paginate_by, в шаблоне —page_objиis_paginated. - Общие блоки (шапка, подвал, пагинация, меню) — во фрагментах
include. - Bootstrap подключаем как статику из
STATICFILES_DIRSили с CDN.
Практика решено 0 из 5
Выведите дерево категорий тегом recursetree (django-mptt): для каждого узла <li> со ссылкой
<a href="{{ node.get_absolute_url }}">{{ node.title }}</a>, а если узел не лист — вложенный <ul> с {{ children }}.
{% load mptt_tags %}
<ul>
{% recursetree categories %}
<li>
<a href="{{ node.get_absolute_url }}">{{ node.title }}</a>
{% if not node.is_leaf_node %}
<ul>{{ children }}</ul>
{% endif %}
</li>
{% endrecursetree %}
</ul>
Напишите тег-включение popular_categories (шаблон includes/categories_menu.html), который возвращает
контекст {"categories": Category.objects.all()}. Объект библиотеки — register.
from django import template
from modules.blog.models import Category
register = template.Library()
@register.inclusion_tag("includes/categories_menu.html")
def popular_categories():
return {"categories": Category.objects.all()}
Почему recursetree выполняет всего один запрос к базе для всего дерева?
Напишите ссылку «Предыдущая» для пагинации ListView (Bootstrap): если page_obj.has_previous, выведите
<li class="page-item"><a class="page-link" href="?page={{ page_obj.previous_page_number }}">Предыдущая</a></li>.
{% if page_obj.has_previous %}
<li class="page-item"><a class="page-link" href="?page={{ page_obj.previous_page_number }}">Предыдущая</a></li>
{% endif %}
Где в шаблоне ListView с paginate_by проверить, нужна ли пагинация вообще?
Войдите, чтобы отмечать пройденные уроки.