Django предоставляет мощную встроенную конструкцию под названием Paginator
, которая помогает упростить обработку разбивки на страницы для ваших моделей и помогает избавиться от шаблонного кода. В этой статье показано, как использовать этот класс во встроенных в Django ListView
и DetailView
CBV на двух простых примерах.
Примеры моделей
Во-первых, давайте определим класс модели и соответствующее представление без нумерации страниц.
В качестве простого примера давайте представим простой вариант использования упаковки коробок с предметами в каждой коробке. Отношение будет представлено как отношение внешнего ключа «один ко многим» в модели Item
, указывающее на модель Box
.
class Box(models.model): name = models.CharField(max_length=128) height = models.IntegerField() width = models.IntegerField() length = models.IntegerField() description = models.TextField() class Item(models.model): box = models.ForeignKey(Box) name = models.CharField(max_length=128) description = models.TextField()
Далее приведены соответствующие виды без разбивки на страницы для модели Box
:
class BoxListView(ListView): model = Box class BoxDetailView(DetailView): model = Box slug_field = 'id' slug_url_kwarg = 'id' context_object_name = 'box'
Теперь давайте разобьем оба вида на страницы, начиная с ListView
.
Пагинация модели в ListView
Первый шаг — разбить представление List
для модели Box
, чтобы список всех блоков был разбит на страницы по 15 блоков на странице.
К счастью, Django упрощает эту задачу, предоставляя свойство paginate_by
в универсальном классе ListView
. Ниже приведен такой пример того, как обновить существующий ListView
для разбиения на страницы полей в группах по 15:
class BoxListView(ListView): model = Box paginate_by = 15
Использование свойства paginate_by
в CBV открывает доступ к новым контекстным переменным в шаблоне, позволяющим отображать страницы, например:
<ul> {% for box in page_obj %} <li>{{ box.name }}</li> {% endfor %} </ul> <br /> {% if page_obj.has_previous %} <a href="?page={{ page_obj.prev_page_number }}">Previous</a> {% endif %} Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }} {% if page_obj.has_next %} <a href="?page={{ page_obj.next_page_number }}">Next</a> {% endif %}
page_obj
в цикле for
является генератором экземпляров модели Box
, и на него можно ссылаться как таковой. Генератор вернет экземпляры только для текущей страницы.
Обратите внимание, что CBV автоматически унаследовал новый параметр HTTP GET, page
, представляющий текущий отображаемый номер страницы.
Разбиение на страницы связанной модели в DetailView
Далее более сложная проблема: при отображении страницы сведений для одного поля давайте разобьем на страницы все содержимое этого поля (представленное моделью Item
) в контексте представления BoxDetails
. Ведь в одной Коробке могут быть сотни предметов.
Ответ заключается в том, чтобы объединить перезапись метода get_context_object
в CBV с классом Paginator
, встроенным в Django:
class BoxDetailView(DetailView): model = Box slug_field = 'box_id' slug_url_kwarg = 'box_id' context_object_name = 'box' def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) box = kwargs.get('object') page_num = self.request.GET.get('page', 1) results = box.item_set.all() paginator = Paginator(results, per_page=15) context['box_contents'] = paginator.get_page(page_num) return context
Обновленный метод добавляет новую переменную в контекстный словарь, отправляемый в шаблон, с именем box_contents
. Эта новая переменная является экземпляром объекта Django Page
, который, как и в предыдущем примере, содержит все содержимое текущей страницы, а также некоторые метаданные о номере текущей страницы, общем количестве страниц, номерах следующей и предыдущей страниц, и так далее.
В шаблоне новую переменную также можно использовать для печати каждого из элементов в поле только для текущей страницы:
<h1>Viewing details for box {{ box.name }}</h1> Box dimensions: {{ box.width }}"W x {{ box.length }}"L x {{ box.height }}"H <br /> Description: {{ box.description }} <br /> <h2>Box contents:</h2> <ul> {% for item in box_contents %} <li>{{ item.name }}</li> {% endfor %} </ul>
Ниже, в том же файле шаблона, можно использовать следующее, чтобы напечатать все номера страниц и выделить текущую страницу жирным шрифтом:
Pages: {% if page_obj.has_previous %} <a href="?page={{ page_obj.prev_page_number }}">Previous</a> {% endif %} {% for page_num in box_contents.paginator.page_range %} {% if page_num == box_contents.number %} <strong>{{ page_num }}</strong> {% else %} <a href="?page={{ page_num }}">{{ page_num }}</a> {% endif %} {% endfor %} {% if page_obj.has_next %} <a href="?page={{ page_obj.next_page_number }}">Next</a> {% endif %}
Удачной пагинации! Для получения дополнительных примеров и более глубокого погружения в тему перейдите по ссылкам ниже.
Рекомендуемые чтения
Я нашел следующие страницы чрезвычайно полезными: