Skip to content

Latest commit

 

History

History
310 lines (204 loc) · 10.3 KB

File metadata and controls

310 lines (204 loc) · 10.3 KB

Модуль 8. Урок 53. Введение в пагинацию. Класс Paginator

На практике почти любой веб-проект сталкивается с одной и той же проблемой: количество данных растёт, а отображать их все на одной странице становится неудобно и неэффективно. Списки фильмов, актёров, режиссёров или жанров в проекте cinemahub — яркий тому пример. Уже при нескольких десятках записей страница начинает перегружаться, ухудшается восприятие информации и падает производительность.

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

В этом уроке мы разберём:

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

Что такое пагинация и зачем она нужна

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

В проекте cinemahub пагинация особенно актуальна для:

  • списка фильмов (Movie);
  • списков актёров и режиссёров;
  • результатов поиска и фильтрации.

Без пагинации:

  • HTML-страницы становятся громоздкими;
  • возрастает время загрузки;
  • навигация по списку становится неудобной.

Знакомство с классом Paginator вне Django

Прежде чем применять пагинацию в представлениях, полезно понять, как работает класс Paginator сам по себе.

Пример данных

Предположим, у нас есть список фильмов:

movies = [
    "Inception", "Interstellar", "The Dark Knight",
    "The Prestige", "Memento", "Dunkirk",
    "Tenet", "Batman Begins", "Oppenheimer"
]

Наша задача — выводить по 3 фильма на страницу.

Создание объекта Paginator

from django.core.paginator import Paginator

paginator = Paginator(movies, 3)

Здесь:

  • первый аргумент — список или queryset;
  • второй аргумент — количество элементов на одной странице.

Основные свойства Paginator

paginator.count        # Общее количество элементов
paginator.num_pages    # Количество страниц
paginator.page_range   # Диапазон номеров страниц

Если вывести значения:

  • count → 9
  • num_pages → 3
  • page_range → [1, 2, 3]

Получение конкретной страницы

Чтобы получить данные для конкретной страницы, используется метод page().

page1 = paginator.page(1)

Теперь объект page1 содержит:

  • page1.object_list — элементы страницы;
  • page1.has_next() — есть ли следующая страница;
  • page1.has_previous() — есть ли предыдущая;
  • page1.next_page_number() — номер следующей страницы.

Этот объект мы и будем передавать в шаблон.


Использование Paginator в Django-представлении

Теперь перенесём эти знания в реальный Django-проект cinemahub.

Задача

Создать страницу со списком опубликованных фильмов, разбитых на страницы по 5 фильмов.


Представление с пагинацией (views.py)

# movies/views.py

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

def movie_list(request):
    movies = Movie.objects.filter(is_published=True).order_by('-created_at')

    paginator = Paginator(movies, 5)
    page_number = request.GET.get('page')
    page_obj = paginator.get_page(page_number)

    context = {
        'page_obj': page_obj,
        'title': 'Список фильмов',
    }
    return render(request, 'movies/movie_list.html', context)

Разберём код пошагово:

  1. Получаем queryset фильмов.

  2. Создаём объект Paginator.

  3. Извлекаем номер страницы из GET-параметра page.

  4. Используем get_page():

    • если номер страницы некорректный — Django покажет первую;
    • если номер слишком большой — последнюю.
  5. Передаём page_obj в шаблон.


Шаблон со списком фильмов и пагинацией

Вывод фильмов (templates/movies/movie_list.html)

<h1>{{ title }}</h1>

<ul>
    {% for movie in page_obj %}
        <li>
            {{ movie.title }} ({{ movie.year }})
        </li>
    {% endfor %}
</ul>

Обратите внимание: page_obj можно использовать как обычный список.


Навигация по страницам

Добавим блок пагинации:

<nav>
    <ul>
        {% for page in page_obj.paginator.page_range %}
            <li>
                <a href="?page={{ page }}">{{ page }}</a>
            </li>
        {% endfor %}
    </ul>
</nav>

Теперь пользователь может переходить между страницами по ссылкам вида:

/movies/?page=2

Проверка результата в браузере

После запуска сервера:

  1. Переходим на страницу списка фильмов.

  2. Видим не более 5 фильмов.

  3. Внизу страницы отображаются номера страниц.

  4. При переходе:

    • ?page=1 — первая страница;
    • ?page=999 — последняя;
    • ?page=abc — первая.

Django обрабатывает эти ситуации автоматически, если используется get_page().


Типичные ошибки и как их избежать

Ошибка 1: Использование page() вместо get_page()

paginator.page(page_number)

Если page_number некорректен — возникнет исключение. Для начального уровня рекомендуется всегда использовать get_page().


Ошибка 2: Передача в шаблон не page_obj, а paginator

В шаблоне нужно работать именно с объектом страницы, а не с Paginator.


Ошибка 3: Отсутствие сортировки queryset

Без order_by() порядок элементов может быть непредсказуемым, особенно при добавлении новых записей.


Практические задания

Задание 1

Создайте представление, которое выводит список актёров, разбитый на страницы по 10 человек.

Требования:

  • использовать Paginator;
  • выводить имя актёра;
  • добавить навигацию по страницам.

Задание 2

Реализуйте пагинацию для списка жанров, по 7 жанров на страницу.


Сравнить решение

Решение задания 1

# actors/views.py

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

def actor_list(request):
    actors = Actor.objects.all().order_by('name')

    paginator = Paginator(actors, 10)
    page_number = request.GET.get('page')
    page_obj = paginator.get_page(page_number)

    return render(request, 'actors/actor_list.html', {'page_obj': page_obj})

Решение задания 2

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

def genre_list(request):
    genres = Genre.objects.all()

    paginator = Paginator(genres, 7)
    page_obj = paginator.get_page(request.GET.get('page'))

    return render(request, 'genres/genre_list.html', {'page_obj': page_obj})

Вопросы

  1. Зачем нужна пагинация в веб-приложениях?
  2. Какие аргументы принимает класс Paginator?
  3. В чём разница между page() и get_page()?
  4. Что содержит объект page_obj?
  5. Как получить номер текущей страницы?
  6. Почему важно сортировать queryset перед пагинацией?
  7. Как формируются ссылки на страницы в шаблоне?
  8. Что произойдёт при передаче некорректного номера страницы?

Предыдущий урок | Следующий урок