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

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

10.5 Вывод дерева категорий, пагинация, добавление Bootstrap 5
3 из 3 шагов пройдено

Для этого первым делом создадим шаблон templates/pagination.html, и добавим в него следующий код:

{% if is_paginated %}
    <div class="pagination p-3">
    {% for page_number in page_obj.paginator.get_elided_page_range %}
        {% if page_number == page_obj.paginator.ELLIPSIS %}
            {{page_number}}
        {% else %}
            <a href="?page={{ page_number }}" class="page-link">
                {{page_number}}
            </a>
        {% endif %}
    {% endfor %}
    </div>
{%endif%}

Давайте разберем его, через условие {% if is_paginated %} мы проверяем активна ли пагинация, чтобы не показывать ничего, когда количество записей меньше чем, указано в представлении.
Далее мы запускаем цикл через метод paginator.get_elided_page_range, он возвращает список номеров страниц, похожий на paginator.page_range, но может добавить многоточие к одной или обеим сторонам текущего номера страницы, когда Paginator.num_pages большой.

Paginator.get_elided_page_range(число, *, on_each_side=3, on_ends=2)

Количество страниц, которые нужно включить с каждой стороны текущего номера страницы, определяется аргументом on_each_side, который по умолчанию равен 3.

Количество страниц, которые нужно включить в начале и конце диапазона страниц, определяется аргументом on_ends, который по умолчанию равен 2.

Например, с значениями по умолчанию для on_each_side и on_ends, если текущая страница 10 и есть 50 страниц, диапазон страниц будет [1, 2, '...', 7, 8, 9, 10, 11, 12, 13, '...', 49, 50].
Это приведет к страницам 7, 8 и 9 слева от и 11, 12 и 13 справа от текущей страницы, а также страницы 1 и 2 в начале и 49 и 50 в конце.

Далее мы проверяем на совпадение номера страницы с paginator.ELLIPSIS, это строка, используемая в качестве замены выброшенных номеров страниц в диапазоне страниц, возвращенном get_elided_page_range(). По умолчанию это '…'. И если она не равна, то выводим номера страниц как ссылки.

И подключим данный файл в main.html:

# остальной шаблон
<div class="col-lg-8 p-4">
    {% block content %}
    {% endblock %}
    {% include 'pagination.html' %}
</div>
# остальной шаблон


Осталось доработать наши представления, для этого в класс PostListView, который мы используем для главной странице и добавим ему параметр paginate_by = 2:

class PostListView(ListView):
    model = Post
    template_name = 'blog/post_list.html'
    context_object_name = 'posts'
    paginate_by = 2

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['title'] = 'Главная страница'
        return context


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

class PostFromCategory(ListView):
    model = Post
    template_name = 'blog/post_list.html'
    context_object_name = 'posts'
    category = None
    paginate_by = 1

    # остальные методы класса

На главной странице мы установили ограничение в 2 записи на странице, а в выводе записей в категориях мы установили ограничение в 1 запись. Конечно эти ограничения далеки от тех, которые необходимо использовать на сайте, но так мы можем протестировать работу пагинации.

Запустим сервер и перейдем на главную страницу:

Мы видим нашу пагинацию внизу страницы.

Перейдем в категории и мы увидим что она у нас прекрасно работает. А в случае если будет всего 1 запись на странице, кнопок с номерами страниц не будет.


На этом разработка пагинации закончена.


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

А как получить доступ в PostListView к параметрам on_each_sideon_ends  и атрибуту Paginator.ELLIPSIS для изменения значений по умолчанию на свои?

Изменен Евгений Куликов

@Евгений_Куликов, Как один из вариантов, в нашем случае наверное будет самым удачным.

class PostFromCategory(ListView):

.....

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['title'] = f'Записи из категории: {self.category.title}'
        page = context['page_obj']
        context['paginator_range'] = page.paginator.get_elided_page_range(page.number, on_each_side=4, on_ends=3)
        return context

И в шаблоне пагинации уже бежим по циклу {% for page_number in paginator_range %}

Изменен Илья Перминов

@Илья_Перминов,  Спасибо за пояснение!