Какой атрибут в представлении позволяет настроить пагинацию
Перейти к содержимому

Какой атрибут в представлении позволяет настроить пагинацию

  • автор:

Добавляем пагинацию (pagination)

На этом занятии мы рассмотрим возможность разбивки списка данных на отдельные страницы. Например, у нас имеется API-запрос на список известных женщин:

Понятно, что в БД может быть огромное количество записей по женщинам и выдавать их все по данному запросу было бы крайне неразумно из-за слишком большого объема данных. Для этого и вводится пагинация, то есть, разбивка на отдельные страницы по несколько записей на каждой. И как это сделать в рамках Django REST Framework мы сейчас увидим.

Конечно, в официальной документации можно подробно почитать, как подключается пагинация к проекту:

Я прямо буду ей следовать. Фактически, все что нам нужно, это прописать в словаре REST_FRAMEWORK, следующие строчки:

REST_FRAMEWORK = { 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination', 'PAGE_SIZE': 2, . }

Ключ ‘DEFAULT_PAGINATION_CLASS’ позволяет задать класс пагинации, который будет применяться по умолчанию к выдаваемым спискам данных. В Django REST Framework имеется встроенный класс LimitOffsetPagination и мы его здесь указываем. Второй ключ ‘PAGE_SIZE’ задает число записей (в нашем случае женщин) на странице. Я указал два, чтобы мы могли видеть пагинацию в действии.

Запустим, теперь, тестовый веб-сервер:

python manage.py runserver

и откроем в браузере страницу:

Как видите, у нас здесь с вами отобразился только первый фрагмент списка из двух записей. Кроме того, в JSON-ответе мы видим параметры:

  • «count» – общее число записей;
  • «next» – ссылка на следующую страницу;
  • «previous» – ссылка на предыдущую страницу;
  • «results» – набор данных.

Также средства DRF позволяют нам в интерактивном режиме в браузере переключаться по страницам и просматривать их содержимое. Однако, обратите внимание, такая пагинация будет автоматически применяться к каждому API-запросу, где идет вывод списка данных. Например, у нас есть запрос для списка зарегистрированных пользователей: http://127.0.0.1:8000/api/v1/auth/users/ и здесь мы тоже видим включенный режим пагинации. Но, если открыть страницу с одной записью: http://127.0.0.1:8000/api/v1/women/9/ то никакой пагинации не будет, т.к. здесь нет списка записей в ответе.

Пользовательские классы пагинации

Конечно, нам может потребоваться для определенных API-запросов настраивать свои параметры пагинации. Это делается достаточно просто с помощью определения собственных классов пагинации и подключения их к нужным видам. Давайте сделаем это для класса WomenAPIList. Вначале определим свой класс пагинации WomenAPIListPagination, который унаследуем от базового класса в DRF – PageNumberPagination:

class WomenAPIListPagination(PageNumberPagination): page_size = 3 page_size_query_param = 'page_size' max_page_size = 10000
  • page_size – число записей на страницу;
  • page_size_query_param – параметр запроса, в котором можно настраивать количество выдаваемых записей на страницу;
  • max_page_size – максимальное количество записей на странице для запроса page_size_query_param.
class WomenAPIList(generics.ListCreateAPIView): queryset = Women.objects.all() serializer_class = WomenSerializer permission_classes = (IsAuthenticatedOrReadOnly, ) pagination_class = WomenAPIListPagination

и в браузере откроем страницу: http://127.0.0.1:8000/api/v1/women/ Видим, что выдается по три записи на странице, как это и настроено в нашем пользовательском классе. Но мы также можем выполнить и такой запрос: http://127.0.0.1:8000/api/v1/women/?page_size=4 Здесь дополнительно прописан параметр page_size со значением 4. Теперь у нас на странице будет по четыре записи. Этот параметр можно использовать, если клиент пожелает изменить число выдаваемых записей на странице. Но их будет не более max_page_size. Например, если этот параметр установить в два:

max_page_size = 2

Пагинация¶

Django предоставляет высокоуровневые и низкоуровневые способы управления постраничными данными — то есть данными, разделенными на несколько страниц, со ссылками «Предыдущая/Следующая».

Класс Paginator ¶

Под капотом все методы пагинации используют класс Paginator . Он выполняет всю тяжелую работу по разбиению QuerySet на Page объекты.

Пример¶

Дайте Paginator список объектов, плюс количество элементов, которые вы хотели бы иметь на каждой странице, и он предоставит вам методы для доступа к элементам для каждой страницы:

>>> from django.core.paginator import Paginator >>> objects = ["john", "paul", "george", "ringo"] >>> p = Paginator(objects, 2) >>> p.count 4 >>> p.num_pages 2 >>> type(p.page_range) >>> p.page_range range(1, 3) >>> page1 = p.page(1) >>> page1 >>> page1.object_list ['john', 'paul'] >>> page2 = p.page(2) >>> page2.object_list ['george', 'ringo'] >>> page2.has_next() False >>> page2.has_previous() True >>> page2.has_other_pages() True >>> page2.next_page_number() Traceback (most recent call last): . EmptyPage: That page contains no results >>> page2.previous_page_number() 1 >>> page2.start_index() # The 1-based index of the first item on this page 3 >>> page2.end_index() # The 1-based index of the last item on this page 4 >>> p.page(0) Traceback (most recent call last): . EmptyPage: That page number is less than 1 >>> p.page(3) Traceback (most recent call last): . EmptyPage: That page contains no results 

Обратите внимание, что вы можете передать Paginator список/кортеж, Django QuerySet или любой другой объект с методом count() или __len__() . При определении количества объектов, содержащихся в переданном объекте, Paginator сначала попытается вызвать count() , затем вернется к использованию len() , если у переданного объекта нет метода count() . Это позволяет таким объектам, как QuerySet Django, использовать более эффективный count() метод, если он доступен.

Пагинация ListView ¶

django.views.generic.list.ListView предоставляет встроенный способ постраничного отображения списка. Вы можете сделать это, добавив атрибут paginate_by к вашему классу представления, например:

from django.views.generic import ListView from myapp.models import Contact class ContactListView(ListView): paginate_by = 2 model = Contact 

Это ограничивает количество объектов на странице и добавляет paginator и page_obj к context . Чтобы пользователи могли перемещаться между страницами, добавьте ссылки на следующую и предыдущую страницу в ваш шаблон следующим образом:

 for contact in page_obj %>  contact.full_name|upper >>br> .  endfor %> div class="pagination"> span class="step-links">  if page_obj.has_previous %> a href="?page=1">« firsta> a href="?page= page_obj.previous_page_number >>">previousa>  endif %> span class="current"> Page  page_obj.number >> of  page_obj.paginator.num_pages >>. span>  if page_obj.has_next %> a href="?page= page_obj.next_page_number >>">nexta> a href="?page= page_obj.paginator.num_pages >>">last »a>  endif %> span> div> 

Использование Paginator в функции представления¶

Вот пример использования Paginator в функции представления для постраничного просмотра набора запросов:

from django.core.paginator import Paginator from django.shortcuts import render from myapp.models import Contact def listing(request): contact_list = Contact.objects.all() paginator = Paginator(contact_list, 25) # Show 25 contacts per page. page_number = request.GET.get("page") page_obj = paginator.get_page(page_number) return render(request, "list.html", "page_obj": page_obj>) 

В шаблоне list.html можно включить навигацию между страницами таким же образом, как и в шаблоне для ListView выше.

Pagination

Pagination – это компонент, используемый для загрузки данных по страницам. Это позволяет пользователям осуществлять навигацию по большому объему данных с предсказуемой производительностью.

См. также компонент SimplePagination, который имеет другое визуальное представление и может использоваться внутри Table , DataGrid и других компонентов-списков.

XML-имя компонента: pagination .

Основы

Pagination содержит номера страниц, что позволяет пользователю выбрать определенную страницу, и выпадающий список для выбора количества элементов на странице.

Чтобы использовать разбивку на страницы вместе с компонентом-списком, необходимо привязать компонент Pagination и компонент-список к одному и тому же источнику данных.

pagination

Привязка к данным

Чтобы создать Pagination , связанный с данными, используйте вложенный элемент containerProvider или loaderProvider .

У Pagination должен быть только один провайдер.

containerProvider

  (1)    (2)  
1 CollectionContainer для сущности Customer .
2 Компонент Pagination связан с источником данных с помощью атрибута dataContainer вложенного элемента containerProvider .

loaderProvider

     (1)     (2)  
1 citiesDl CollectionLoader загружает коллекцию сущностей по JPQL-запросу.
2 Компонент Pagination связан с источником данных с помощью атрибута loaderId вложенного элемента loaderProvider .

Количество элементов на странице

У Pagination есть специальный ComboBox со списком параметров для ограничения количества элементов на одной странице. Чтобы сделать его видимым, установите для атрибута itemsPerPageVisible значение true . Значение по умолчанию – false .

pagination items per page combo box

Значение по умолчанию для этого списка указано в свойстве jmix.ui.component.pagination-items-per-page-options.

Можете настроить свой список параметров, используя атрибут itemsPerPageOptions . Значением атрибута должен быть список параметров, разделенных запятыми:

Значения, меньшие или равные 0 , игнорируются. Значения, превышающие максимальное количество загружаемых записей заменяются на данное максимальное значение.

Используйте атрибут itemsPerPageDefaultValue чтобы задать значение по умолчанию из списка параметров:

Атрибут itemsPerPageUnlimitedOptionVisible задает видимость неограниченного (null) значения параметра в списке ComboBox . Значение по умолчанию – true .

Если выбран параметр null , компонент попытается загрузить все данные которые возможно при текущем ограничении на максимальное количество загружаемых записей.

Максимальное количество загружаемых записей для всех сущностей определено с помощью свойства jmix.ui.default-max-fetch-size. Значение по умолчанию для этого свойства — 10000 . У конкретной сущности может быть другое значение максимального количество загружаемых записей, заданное с помощью свойства jmix.ui.entity-max-fetch-size.

Количество видимых страниц

Компонент Pagination позволяет изменять количество максимально видимых страниц с помощью атрибута maxVisiblePages . Страниц в компонент может быть много, но пользователи будут видеть сразу несколько страниц, в соответствии с атрибутом maxVisiblePages . Значение по умолчанию – 5 . Например, если установить maxVisiblePages=»3″ , будет видно только три страницы одновременно:

pagination max visible pages

События и слушатели

Чтобы сгенерировать заглушку слушателя в Jmix Studio, выберите компонент в XML-дескрипторе экрана или на панели иерархии Jmix UI и используйте вкладку Handlers на панели инспектора Jmix UI.

В качестве альтернативы вы можете воспользоваться кнопкой Generate Handler на верхней панели контроллера экрана.

PageChangeEvent

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

Пример подписки на событие компонента Pagination , объявленного в XML с идентификатором pagination id:

@Subscribe("pagination") public void onPaginationPageChange(Pagination.PageChangeEvent event)

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

BeforeRefreshEvent

BeforeRefreshEvent отправляется перед обновлением данных, когда пользователь нажимает «следующая», «предыдущая» и т.д. Можно предотвратить обновление контейнера данных, вызвав метод preventRefresh() , например:

@Subscribe("paginationWithDefault") public void onPaginationWithDefaultBeforeRefresh(PaginationComponent.BeforeRefreshEvent event) < if (event.getSource().getDataBinder().getCount() >10) (1) event.preventRefresh(); (2) >
1 Проверьте количество экземпляров в хранилище данных.
2 Предотвратите обновление данных.

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

AfterRefreshEvent

AfterRefreshEvent вызывается при обновлении данных.

Пример подписки на событие компонента Pagination , объявленного в XML с идентификатором paginationWithDefault :

@Subscribe("paginationWithDefault") public void onPaginationWithDefaultAfterRefresh(PaginationComponent.AfterRefreshEvent event)

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

TotalCountDelegate

TotalCountDelegate – это слушатель, который используется для получения общего количества элементов. Например:

@Install(to = "pagination", subject = "totalCountDelegate") private Integer paginationTotalCountDelegate()

Чтобы создать слушателя TotalCountDelegate программно, используйте метод setTotalCountDelegate() .

Все XML-атрибуты

Просматривать и редактировать атрибуты, применимые к компоненту, можно с помощью панели инспектора Jmix UI в конструкторе экранов Studio.

This page was built using the Antora default UI.

The source code for this UI is licensed under the terms of the MPL-2.0 license.

Пагинация ¶

Django предоставляет высокоуровневые и низкоуровневые способы управления данными, разбитыми на страницы, то есть данными, которые разделены на несколько страниц со ссылками «Предыдущий / Следующий».

Paginator Класс ¶

Под капотом все методы разбивки на страницы используют этот Paginator класс. Он делает всю тяжелую работу по фактическому разбиению QuerySet на Page объекты.

Пример ¶

Дайте Paginator список объектов, а также количество элементов, которые вы хотите разместить на каждой странице, и получите методы для доступа к элементам для каждой страницы:

>>> from django.core.paginator import Paginator >>> objects = ['john', 'paul', 'george', 'ringo'] >>> p = Paginator(objects, 2) >>> p.count 4 >>> p.num_pages 2 >>> type(p.page_range) >>> p.page_range range(1, 3) >>> page1 = p.page(1) >>> page1 >>> page1.object_list ['john', 'paul'] >>> page2 = p.page(2) >>> page2.object_list ['george', 'ringo'] >>> page2.has_next() False >>> page2.has_previous() True >>> page2.has_other_pages() True >>> page2.next_page_number() Traceback (most recent call last): . EmptyPage: That page contains no results >>> page2.previous_page_number() 1 >>> page2.start_index() # The 1-based index of the first item on this page 3 >>> page2.end_index() # The 1-based index of the last item on this page 4 >>> p.page(0) Traceback (most recent call last): . EmptyPage: That page number is less than 1 >>> p.page(3) Traceback (most recent call last): . EmptyPage: That page contains no results 

Обратите внимание, что вы можете Paginator указать список / кортеж, Django QuerySet или любой другой объект с помощью метода count() или __len__() . При определении количества объектов, содержащихся в переданном объекте, Paginator сначала будет попытаться вызвать count() , а затем вернуться к использованию, len() если переданный объект не имеет count() метода. Это позволяет таким объектам, как Django, QuerySet использовать более эффективный count() метод, когда он доступен.

Пагинация ListView ¶

django.views.generic.list.ListView предоставляет встроенный способ разбивки отображаемого списка на страницы. Вы можете сделать это, добавив paginate_by атрибут в свой класс представления, например:

from django.views.generic import ListView from myapp.models import Contact class ContactListView(ListView): paginate_by = 2 model = Contact 

Это ограничивает количество объектов на странице и добавляет paginator и page_obj к context . Чтобы пользователи могли перемещаться между страницами, добавьте в свой шаблон ссылки на следующую и предыдущую страницу следующим образом:

 for contact in page_obj %>  contact.full_name|upper >>br> .  endfor %> div class="pagination"> span class="step-links">  if page_obj.has_previous %> a href="?page=1">« firsta> a href="?page= page_obj.previous_page_number >>">previousa>  endif %> span class="current"> Page  page_obj.number >> of  page_obj.paginator.num_pages >>. span>  if page_obj.has_next %> a href="?page= page_obj.next_page_number >>">nexta> a href="?page= page_obj.paginator.num_pages >>">last »a>  endif %> span> div> 

Использование Paginator в функции просмотра ¶

Вот пример использования Paginator функции просмотра для разбивки набора запросов на страницы:

from django.core.paginator import Paginator from django.shortcuts import render from myapp.models import Contact def listing(request): contact_list = Contact.objects.all() paginator = Paginator(contact_list, 25) # Show 25 contacts per page. page_number = request.GET.get('page') page_obj = paginator.get_page(page_number) return render(request, 'list.html', 'page_obj': page_obj>) 

В шаблоне list.html вы можете включить навигацию между страницами так же, как в шаблоне для ListView вышеупомянутого.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *