Пагинация с классом ListView

Курс по Django: https://stepik.org/a/183363

Архив проекта: 58_sitewomen.zip

На этом занятии мы с вами добавим пагинацию в класс представления ListView. В нем уже встроен механизм постраничного вывода списков. Согласно документации все, что нам нужно, это определить атрибут:

paginate_by = N

указав число N – количество элементов на одной странице. Например, если в классе WomenHome прописать этот атрибут:

class WomenHome(DataMixin, ListView):
    template_name = 'women/index.html'
    context_object_name = 'posts'
    paginate_by = 3

то получим разбивку по три поста на страницу. Если теперь открыть главную страницу, то именно первые три записи мы и увидим. Но у нас пока нет отображения номеров страниц. Давайте добавим их. Для этого вначале в шаблоне base.html добавим блок навигации после блока content:

{% block navigation %}
{% endblock %}

Затем, в шаблоне index.html пропишем содержимое этого блока следующим образом:

<nav class="list-pages">
    <ul>
        {% for p in paginator.page_range %}
        <li class="page-num">
            <a href="?page={{ p }}">{{ p }}</a>
        </li>
        {% endfor %}
    </ul>
</nav>

Обратите внимание, на уровне шаблонов у нас появляются специальные объекты paginator и page_obj. В частности, с помощью paginator мы формируем отображение ссылок на страницы.

Отлично, если теперь обновить главную страницу сайта, то увидим номера страниц в виде списка вместе со стилями, которые подключены через классы оформлений list-pages и page-num. Сами стили я дополнительно определил в файле styles.css и, если интересно, то вы сможете их самостоятельно посмотреть в проекте, скачав его с github.

Итак, главная страница отображается в нужном нам виде, но если открыть отдельные рубрики, то пагинация пропадает. Почему? Если мы вернемся в проект, то увидим, что за это отвечает отдельный класс WomenCategory, в котором, по идее, также нужно прописать

paginate_by = 3

Однако это будет дублирование кода и нам проще эту строчку записать в общем классе DataMixin:

class DataMixin:
    paginate_by = 3
...

А из класса WomenHome ее убрать. Теперь, на всех страницах, унаследованных от ListView, мы будем видеть автоматическую пагинацию. Правда, везде будет отображаться по три статьи. В данном случае нас это устраивает, но если на разных страницах нужно разное число рубрик при пагинации, то, конечно, в каждом классе отдельно следует прописать атрибут paginate_by со своим значением.

Однако если сейчас в paginate_by указать большое число, например, 30, то на главной странице будет отображаться внизу один единственный номер, что не очень хорошо. Было бы лучше, при одной единственной странице вообще не выводить никаких номеров. Для этого, в шаблоне index.html вывод пагинации будем осуществлять по следующему условию:

{% if page_obj.has_other_pages %}
<nav class="list-pages">
...
</nav>
{% endif %}

Все, теперь в базовый вариант отображения номеров страниц у нас есть. Далее, сделаем еще некоторые улучшения. Например, нам следовало бы убирать ссылку на текущий номер страницы. Для этого в шаблоне index.html мы, при выводе ссылок, будем проверять, если это текущий номер, то отображать его как текст:

<nav class="list-pages">
    <ul>
        {% for p in paginator.page_range %}
                   {% if page_obj.number == p %}
        <li class="page-num page-num-selected">{{ p }}</li>
                   {% else %}
        <li class="page-num">
            <a href="?page={{ p }}">{{ p }}</a>
        </li>
                   {% endif %}
        {% endfor %}
    </ul>
</nav>

Здесь используется свойство number объекта page_obj для определения номера текущей страницы, и если этот номер равен p, то его следует отобразить как текст. Иначе, идет отображение в виде ссылки.

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

Для начала, в админ-панели отметим все статьи для публикации (чтобы номеров страниц было побольше) и в классе DataMixin установим атрибут:

paginate_by = 2

Теперь на главной странице у нас появляется семь номеров. Но, мы решили отображать не все их, а, допустим, два справа и два слева:

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

        {% for p in paginator.page_range %}
                   {% if page_obj.number == p %}
        <li class="page-num page-num-selected">{{ p }}</li>
                   {% elif p >= page_obj.number|add:-2 and p <= page_obj.number|add:2  %}
        <li class="page-num">
            <a href="?page={{ p }}">{{ p }}</a>
        </li>
                   {% endif %}
        {% endfor %}

Все, теперь, обновляя страницу сайта, мы увидим по два номера слева и справа.

Следующим нашим улучшением будет отображение ссылок для перехода на предыдущую и следующую страницы. Это делается очень просто. В шаблоне index.html до цикла запишем такие строчки:

{% if page_obj.has_previous %}
<li class="page-num">
         <a href="?page={{ page_obj.previous_page_number }}">&lt;</a>
</li>
{% endif %}

Мы здесь вначале проверяем, имеет ли смысл вообще выводить ссылку на предыдущую страницу (для первых номеров это не нужно делать). И, если она нужна, то отображаем как элемент списка с номером страницы согласно атрибуту previous_page_number.

То же самое делаем и для отображения ссылки на следующую страницу. Только теперь добавляем пункт после цикла for:

{% if page_obj.has_next %}
<li class="page-num">
         <a href="?page={{ page_obj.next_page_number }}">&gt;</a>
</li>
{% endif %}

Здесь также вначале идет проверка: нужно ли отображать ссылку. Если проверка проходит, то отображаем пункт списка со ссылкой согласно атрибуту next_page_number.

Все, можно обновить главную страницу сайта и увидим эти дополнительные элементы управления. Вот так, в базовом варианте, можно работать с пагинацией во фреймворке Django.

Курс по Django: https://stepik.org/a/183363

Видео по теме