Добавление постраничной разбивки
Когда вы начнете добавлять контент в свой блог, вы сможете легко хранить десятки и даже сотни постов в своей базе данных.
Возможно, вы захотите разделить список постов на несколько страниц, не отображая все записи на одной странице, и вставить навигационные ссылки на разные страницы.
Эта функциональность называется постраничной разбивкой, и ее можно найти почти в каждом веб-приложении, которое показывает длинные списки элементов.
Например, Яндекс использует постраничную разбивку с целью распределения результатов поиска по нескольким страницам.
На рисунке ниже показаны постранично разбитые ссылки Яндекса на страницы результатов поиска:
В Django есть встроенный класс постраничной разбивки, который позволяет легко управлять постранично разбитыми данными, при этом имеется возможность определять число объектов, которое необходимое возвращать в расчете на страницу, и извлекать записи, соответствующие запрошенной пользователем странице.
Класс Paginator первым элементом может принимать список, кортеж, QuerySet или другой объект, который можно оценить с помощью метода count() или __len__(). Для правильной разбивки на страницы, данные должны быть упорядочены, например, с помощью метода order_by() или с порядком по умолчанию в модели.
Добавление постраничной разбивки в представление списка постов
Отредактируйте файл views.py приложения blog, импортировав класс Django Paginator и видоизменив представление post_list, как показано ниже:
from django.shortcuts import render, get_object_or_404
from .models import Post
from django.core.paginator import Paginator
def post_list(request):
post_list = Post.published.all()
# Постраничная разбивка с 3 постами на страницу
paginator = Paginator(post_list, 3)
page_number = request.GET.get('page', 1)
posts = paginator.page(page_number)
return render(request,
'blog/post/list.html',
{'posts': posts})
#........
Давайте рассмотрим новый исходный код, который был добавлен в представление.
-
Мы создаем экземпляр класса
Paginatorс числом объектов, возвращаемых в расчете на страницу. Мы будем отображать по три поста на страницу. -
Мы извлекаем HTTP GET-параметр
pageи сохраняем его в переменнойpage_number. Этот параметр содержит запрошенный номер страницы. Если параметра page нет в GET-параметрах запроса, то мы используем стандартное значение1, чтобы загрузить первую страницу результатов. -
Мы получаем объекты для желаемой страницы, вызывая метод
page()классаPaginator. Этот метод возвращает объектPage, который хранится в переменнойposts. -
Мы передаем номер страницы и объект
postsв шаблон.
Создание шаблона постраничной разбивки
Далее необходимо создать навигацию по страницам, чтобы пользователи имели возможность просматривать разные страницы.
Мы создадим шаблон отображения постранично разбитых ссылок и сделаем его типовым, чтобы иметь возможность переиспользовать шаблон для постраничной разбивки любого объекта на веб-сайте.
Внутри каталога templates создайте новый файл и назовите его pagination.html.
Добавьте в файл следующий ниже исходный код HTML:
<div class="pagination">
<span class="step-links">
{% if page.has_previous %} # если есть предыдущая страница то показываем ее кнопку
<a href="?page={{ page.previous_page_number }}">Previous</a> # возвращает номер предыдущей страницы
{% endif %}
<span class="current">
Page {{ page.number }} of {{ page.paginator.num_pages }}.
</span>
{% if page.has_next %} # если есть следующая страница то показываем ее кнопку
<a href="?page={{ page.next_page_number }}">Next</a> # возвращает номер следуюущей страницы
{% endif %}
</span>
</div>
Это типовой шаблон постраничной разбивки. В котором используются следующие атрибуты:
page.has_previous: проверяет, если есть предыдущая страница возвращаетTrue, иначеFalsepage.has_next: проверяет, если есть следующая страница возвращаетTrue, иначеFalsepage.previous_page_number: возвращает номер предыдущей страницы, вызывает ошибкуInvalidPage, если следующая страница не существует.page.next_page_number: возвращает номер следующей страницы, вызывает ошибкуInvalidPage, если следующая страница не существует.page.number: возвращает номер текущей страницы.
Предполагается, что данный шаблон будет иметь в контексте объект Page, чтобы прорисовывать предыдущую и следующую ссылки, а также отображать текущую страницу и общее число страниц результатов.
Давайте вернемся к шаблону blog/post/list.html и разместим шаблон pagination.html
в нижней части блока {% content %}, как показано ниже:
{% 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="date">
Published {{ post.publish }} by {{ post.author }}
</p>
{{ post.body|truncatewords:30|linebreaks }}
{% endfor %}
{% include "pagination.html" with page=posts %}
{% endblock %}
Шаблонный тег {% include %} загружает данный шаблон и прорисовывает его с использованием текущего контекста шаблона.
Ключевое слово with используется для того, чтобы передавать дополнительные контекстные переменные в шаблон.
Для прорисовки в шаблоне постраничной разбивки используется переменная page, при этом объект Page, который мы передаем из представления в шаблон, называется posts.
Мы используем выражение with page=posts, чтобы передавать переменную, ожидаемую шаблоном постраничной разбивки.
Описанному методу можно следовать для применения шаблона постраничной разбивки для любого типа объекта.
Давайте запустим сервер и откроем http://127.0.0.1:8000/admin/blog/post/ и создадим 4 разных поста. Проверьте, чтобы у всех этих постов был установлен статус Published. Напоминаем, что удобно брать шаблонный текст из Яндекс-Рефератов.
Теперь пройдите по URL-адресу http://127.0.0.1:8000/blog/ в своем браузере. Вы должны увидеть первые три поста в обратном хронологическом порядке, а затем навигационные ссылки в нижней части списка постов, как показано ниже:
Если кликнуть по Next(Далее), то можно увидеть последний пост.
URL-адрес второй страницы содержит GET-параметр ?page=2. Указанный параметр используется представлением для загрузки запрошенной страницы результатов с использованием постраничного разбивщика:
Отлично! Ссылки на постраничную разбивку работают так, как и ожидалось.