Создание сайта на Django: Урок 15, добавление пагинации в Django
avatar
7 | (offline)
❤️‍🔥Notehunter Developer
Добавлено:
Категория: Руководства «Django»
Комментариев: 7

Добавление пагинации в Django

Добавлять пагинацию мы будем в представления ListView, где выводится список наших статей. 

modules/blog/views/articles.py

class ArticleListView(ListView):
    model = Article
    template_name = 'modules/blog/articles/article-list.html'
    context_object_name = 'articles'
    paginate_by = 10

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

Пояснения:

  • Я добавил параметр paginate_by = 10, это значит, что на странице у нас будет ограничение в 10 статей. Но так как у меня две статьи написано, а другие я пока добавлять не хочу, то поставлю 1 для примера.

Пример для представлений основанных на функциях:

from django.core.paginator import Paginator, PageNotAnInteger, EmptyPage
from django.shortcuts import render

def article_list(request):
    object_list = Article.objects.all()
    paginator = Paginator(object_list, 1)  # 3 поста на каждой странице
    page_obj = request.GET.get('page')
    try:
        articles = paginator.page(page_obj)
    except PageNotAnInteger:
        articles = paginator.page(1)
    except EmptyPage:
        articles = paginator.page(paginator.num_pages)
    return render(request, 'modules/blog/articles/article-list.html',  {'page_obj': page_obj, 'articles': articles})

Пояснения:

  • Paginator - это экземпляр класса с кол-вом отображаемых объектов на странице
  • PageNotAnInteger - Если страница не является целым числом, поставим первую страницу
  • EmptyPage - Если страница больше максимальной, доставить последнюю страницу результатов

И так, давайте создадим саму пагинацию в шаблоне. Для этого я создам в templates -> pagination.html и подключу его в main.html

templates/pagination.html

{% if is_paginated %}
<nav aria-label="Page navigation">
   <ul class="pagination border-0">
      {% if page_obj.previous_page_number != 1 %}
      <li class="page-item">
         <a class="page-link" href="?page={{ page_obj.previous_page_number }}">Назад</a>
      </li>
      {% endif %}
      {% for p in paginator.page_range %}
      {% if page_obj.number == p %}
      <li class="page-item active"><a class="page-link" disabled>{{ p }}</a></li>
      {% elif p >= page_obj.number|add:-2 and p <= page_obj.number|add:2  %}
      <li class="page-item"><a class="page-link" href="?page={{ p }}">{{ p }}</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>
      {% endif %}
   </ul>
</nav>
{% endif %}

Пояснения:

  • Если страница с пагинацией, мы показываем панель переходов.
  • Настроил стили bootstrap
  • p - это наши странички из page_range пагинатора

Теперь подключим в main.html

templates/main.html

<!DOCTYPE html>
<html lang="ru">
<head>
    {% load static %}
    <meta charset="UTF-8">
    <title>{{ title }}</title>
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <!-- INCLUDE CSS -->
    <link href="{% static 'plugins/bootstrap-5.2.0-dist/css/bootstrap.min.css' %}" type="text/css" rel="stylesheet">
</head>
<body>
<div class="container">
    <div class="row">
        <div class="col-md-8">
            {% block content %}

            {% endblock %}
            {% include 'pagination.html' %}
        </div>
        <div class="col-md-4">
            some...
        </div>
    </div>
</div>
<script src="{% static 'plugins/bootstrap-5.2.0-dist/js/bootstrap.bundle.min.js' %}"></script>
</body>
</html>

Если вы подключаете к представлению на основе функции, то подключайте так:

{% include 'pagination.html' with page_obj=articles %}

Пояснения:

  • Добавил row col-md-8 col-md-4 для разметки
  • Подключил нашу пагинацию

Если сделали все правильно, то получится как на скриншоте ниже:

Комментарии к статье 7
  • slug2
    5 декабря 2022 г. 23:29

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

    Такой вопрос. Уже второй вечер пытаюсь по примеру сделать пагинацию, но все без успешно. Прошу посмотреть что я не так делаю. Вот мой views.py - https://pastebin.com/TukkfFzt Вот пагинатор - https://pastebin.com/tFfDThh1 А вот шаблон куда я его подключаю - https://pastebin.com/1H7Cn6d3 И вот что у меня получается - https://disk.yandex.ru/i/O4JpqnGf99XK3g

    Ошибок не вылезает

    • Razilator
      6 декабря 2022 г. 7:21

      slug2, доброе утро. Попробуйте убрать код на основе функций, и оставить именно

      class ArticleListView(ListView):
          model = Article
          template_name = 'blog/blog.html'
          context_object_name = 'articles'
          paginate_by = 2
      
          def get_context_data(self, **kwargs):
              context = super().get_context_data(**kwargs)
              context['title'] = 'Главная страница'
              return context
      

      и следующий код в шаблоне для пагинации:

       {% if is_paginated %}
                  <nav aria-label="Page navigation">
                    <ul class="pagination border-0">
                       {% if page_obj.previous_page_number != 1 %}
                          <li class="page-item">
                            <a class="page-link" href="?page={{ page_obj.previous_page_number }}">Назад</a>
                          </li>
                      {% endif %}
                      {% for p in paginator.page_range %}
                          {% if page_obj.number == p %}
                              <li class="page-item active"><a class="page-link" disabled>{{ p }}</a></li>
                              {% elif p >= page_obj.number|add:-2 and p <= page_obj.number|add:2  %}
                               <li class="page-item"><a class="page-link" href="?page={{ p }}">{{ p }}</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>
                      {% endif %}
                    </ul>
                  </nav>
          {% endif %}
      

      И подключение без with

      {% include 'pagination.html' %}
      
      • slug2
        6 декабря 2022 г. 22:48

        Razilator, Да, спасибо! Так заработало. Я просто немного не разобрался в статье. Единственное, не работает кнопка назад. Кнопка вперед работает, а вот назад не активна. https://disk.yandex.ru/d/IiAvJXSApoJPnQ

        • Razilator
          7 декабря 2022 г. 7:50

          slug2, очень странно, ведь я скопировал вам код с моего сайта :D, а у меня кнопка назад работает.

          • slug2
            8 декабря 2022 г. 22:07

            Razilator, все, нашел. У меня был класс active указан для этого элемента. Спасибо большое за помощь.

          • slug2
            8 декабря 2022 г. 23:27

            Razilator, не нашел у вас решения для создания ссылок на соседние посты, типа предыдущий пост или следующий пост в конце статьи. Нагуглил про get_next_by_FOO, но не совсем понимаю как его применить.

            • Razilator
              9 декабря 2022 г. 8:12

              slug2, я если честно о таком даже не знал, что есть :D. Это конечно реализовать можно, но будет ли смысл? Это как известно, доп.запросы, ибо нам нужно вытащить ссылку на статью следующую и предыдущую от текущего объекта. Вроде есть такой модуль: django-next-prev, но не уверен, что он актуален.

Форма добавления комментария (необходима регистрация)