Программы
Добавляем постраничную пагинацию на Django сайт

Добавляем постраничную пагинацию на Django сайт

На сайтах часто встречаются многостраничные объекты: список товаров, список заметок и т.д. Поэтому важно уметь добавить навигацию по страницам на Django-проекте.

Как и многое во фреймворке, стандартные задачи в Django решаются довольно просто стандартными средствами. Трудности возникают разве что при кастомизации стандартных решений. В этой заметке мы рассмотрим лишь пагинацию по одной модели (что в подавляющем большинстве таковой и является).

Итак, представим себе, что мы имеем модель Post, имеющей примерно следующий вид:

from django.db import models


class Post(models.Model):
    name = models.CharField(max_length=100, verbose_name='Название')
    slug = models.CharField(max_length=64, verbose_name='URL', validators=[validate_slug], unique=True)
    published = models.DateTimeField(default=None, null=True, blank=True)
    text = models.TextField(verbose_name='Текст')

Тогда контроллер для вывода списка заметок будет выглядеть примерно следующим образом:

from django.views.generic import ListView
from post.models import Post


class PostListView(ListView):
    model = Post

Шаблон же для страницы с листингом заметок будет выглядеть следующим образом (в общем виде):

<div class="post-list">
{% for object in object_list %}
    <div class="post">
        <h3>{{ object.name }}</h3>
        <p>
            {{ object.text|truncatewords_html:10 }}
        </p>
    </div>
{% endfor %}
</div>

Итак, мы описали в общем виде наш MVC. Чтобы добавить к нашему листингу пагинацию, добавим атрибут paginate_by для класса контроллера:

from django.views.generic import ListView
from post.models import Post


class PostListView(ListView):
    model = Post
    paginate_by = 20  # Вот эта строчка

Теперь в контексте нашего шаблона появляется объект page_obj - объект для страницы с пагинацией. Теперь давайте его используем для вывода ссылок для навигации между страницами:

<div class="post-list">
{% for object in object_list %}
    <div class="post">
        <h3>{{ object.name }}</h3>
        <p>
            {{ object.text|truncatewords_html:10 }}
        </p>
    </div>
{% endfor %}

    <div class="example1-pagination">
        {% if page_obj.has_previous %}
            <a class="example1-pagination_link" href="?page=1">&laquo; первая</a>
            <a class="example1-pagination_link" href="?page={{ page_obj.previous_page_number }}">предыдущая</a>
        {% endif %}

        <span class="example1-pagination_link example1-pagination_link__active">
            Страница {{ page_obj.number }} из {{ page_obj.paginator.num_pages }}.
        </span>

        {% if page_obj.has_next %}
            <a class="example1-pagination_link" href="?page={{ page_obj.next_page_number }}">следующая</a>
            <a class="example1-pagination_link" href="?page={{ page_obj.paginator.num_pages }}">последняя &raquo;</a>
        {% endif %}
    </div>
</div>

Таким образом, мы получаем верстку пагинации примерно следующего формата:

Естественно, можно и иначе вывести пагинацию. Так, к примеру, можно показывать первые три страницы, по три страницы вокруг текущей и три последние страницы. Такой подход мне кажется более удобным для использования:

<div class="post-list">
{% for object in object_list %}
    <div class="post">
        <h3>{{ object.name }}</h3>
        <p>
            {{ object.text|truncatewords_html:10 }}
        </p>
    </div>
{% endfor %}

    <div class="example2-pagination">
        {% for num in page_obj.paginator.page_range %}
            {% if num < page_obj.number and num < 4 %}
                <a class="example2-pagination_link" href="?page={{ num }}">{{ num }}</a>
            {% elif num < page_obj.number and num > page_obj.number|add:-4 %}
                <a class="example2-pagination_link" href="?page={{ num }}">{{ num }}</a>
            {% elif num == page_obj.number %}
                <span class="example2-pagination_link example2-pagination_link__active">{{ num }}</span>
            {% elif num > page_obj.number and num < page_obj.number|add:4 %}
                <a class="example2-pagination_link" href="?page={{ num }}">{{ num }}</a>
            {% elif num > page_obj.number and num > page_obj.paginator.num_pages|add:-3 %}
                <a class="example2-pagination_link" href="?page={{ num }}">{{ num }}</a>
            {% endif %}
        {% endfor %}
    </div>
</div>

Тогда получим примерно следующий вид:

1 2 3 4 5 6 7 48 49 50

Если же вы используете не Class Based View, а обычные view в виде функций, то подготовить объект для пагинации придётся нам самим:

from django.shortcuts import render
from django.core.paginator import Paginator

from post.models import Post


def post_list_view(request):
    paginator = Paginator(Post.objects.all(), per_page=20)
    page = paginator.page(request.GET.get('page', 1))
    return render(request, 'post/post_list.html', {
        'object_list': page.object_list,
        'page_obj': page,
    })

Подготовив в нашем view таким образом контекст шаблона, нам не нужно беспокоиться об изменениях самих шаблонов, ведь мы передали в них ровно то, что передал бы и его брат-ClassBasedView, что мы рассмотрели выше.

Также может быть вам интересно:

Новый оператор match-case в Python

В новой версии Python (3.10) появится новый оператор. Новый оператор сопоставления по шаблону (match-case).

Читать »

Новый синтаксис старой команды with в Python 3.10

Как же долго моё чувство прекрасного страдало… Но в Python 3.10 появился новый парсер синтаксических конструкций Python!

Читать »
Фото Как настроить отправку почты из Django

Как настроить отправку почты из Django

Письма об ошибках, отчёты на почту, восстановление паролей - всё это полезно при работе с сайтом. Django предоставляет удобный способ это сделать с минимумом настроек!

Фото Добавляем поддержку медиа-файлов в Django проект

Добавляем поддержку медиа-файлов в Django проект

Современные сайты редко ограничиваются только текстом и вёрсткой. Часто в заметках красуются фотографии, а рядом с описанием товаров - их изображения.

Фото Настройка журналирования (логирования) в Python с примерами

Настройка журналирования (логирования) в Python с примерами

Во время работы программы часто нужно сохранять некоторые важные записи о процессе выполнения команды. В Python есть довольно мощный модуль для работы с логами - давайте разберёмся с тем, как его использовать.

Фото Шаблон разработки ПО — Model View Controller (MVC)

Шаблон разработки ПО — Model View Controller (MVC)

MVC - один из самых распространённых архитектурных шаблонов разработки. Часто используется в различных фреймворках. В том числе и в Django.

Фото Пример своей консольной команды в Django проекте

Пример своей консольной команды в Django проекте

Если вы работали с Django проектом, то, скорее всего, запускали команды из консоли (manage.py). В Django есть простой способ писать свои команды для управления проектом.

Фото Разграничение прав доступа на Django сайте

Разграничение прав доступа на Django сайте

Почти на любом веб-сайте необходимо разделять пользователей на группы и предоставлять им разные возможности. В Django есть довольно серьёзная система прав доступа для пользователей - давайте её рассмотрим!

Фото Пользователи и их создание в Django - своя регистрация на сайте

Пользователи и их создание в Django - своя регистрация на сайте

Если вашим сайтом должны активно пользоваться несколько человек, то полезно их различать, а значит - надо уметь создавать пользователей, либо предоставлять возможность регистрации Django пользователей.

Фото Как на Bash посчитать число строк в проекте (директории)

Как на Bash посчитать число строк в проекте (директории)

Ниже будет представлен однострочник, решающий данную задачу на Bash + пошаговое описание его работы.