Pagination
Paginator splits a queryset or list into fixed-size pages.
from buraq.paginator import PaginatorBasic usage
Section titled “Basic usage”paginator = Paginator(Post.objects.filter(is_published=True), per_page=10)page = await paginator.page(1)
for post in page: print(post.title)
print(paginator.count) # total objects across all pagesprint(paginator.num_pages) # total number of pagesprint(page.number) # current page number (1-based)Page navigation
Section titled “Page navigation”page = await paginator.page(2)
page.has_previous() # Truepage.has_next() # True / Falsepage.has_other_pages() # True if there is any adjacent page
page.previous_page_number() # 1page.next_page_number() # 3
page.start_index() # 1-based index of first item on this pagepage.end_index() # 1-based index of last item on this pagePage range
Section titled “Page range”Iterate over all page numbers (useful for rendering page links):
for page_num in paginator.page_range: # range(1, num_pages + 1) print(page_num)In a view
Section titled “In a view”from buraq.paginator import Paginator, EmptyPage, PageNotAnInteger
async def post_list(request): page_number = request.query_params.get("page", 1) paginator = Paginator(Post.objects.all(), per_page=20)
try: page = await paginator.page(page_number) except PageNotAnInteger: page = await paginator.page(1) except EmptyPage: page = await paginator.page(paginator.num_pages)
return templates.TemplateResponse(request, "posts/list.html", {"page": page})Template:
{% for post in page %} <h2>{{ post.title }}</h2>{% endfor %}
{% if page.has_previous() %} <a href="?page={{ page.previous_page_number() }}">Previous</a>{% endif %}
Page {{ page.number }} of {{ page.paginator.num_pages }}
{% if page.has_next() %} <a href="?page={{ page.next_page_number() }}">Next</a>{% endif %}Paginating plain lists
Section titled “Paginating plain lists”Paginator works with any sequence, not just querysets:
items = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]paginator = Paginator(items, per_page=3)page = await paginator.page(2)list(page) # [4, 5, 6]Options
Section titled “Options”Paginator( object_list, per_page=10, orphans=0, # if last page has ≤ orphans items, merge into previous page allow_empty_first_page=True # allow page 1 even when there are no objects)Exceptions
Section titled “Exceptions”| Exception | Raised when |
|---|---|
PageNotAnInteger |
Page number cannot be converted to an integer |
EmptyPage |
Page number is out of range |
InvalidPage |
Base class for the above two |
AsyncPaginator
Section titled “AsyncPaginator”AsyncPaginator is an explicitly async-only variant of Paginator. It always awaits the queryset count before slicing, making it cleaner in codebases where you want to be explicit about async behaviour.
from buraq.paginator import AsyncPaginator, AsyncPage
paginator = AsyncPaginator(Post.objects.filter(published=True), per_page=10)page = await paginator.page(1) # returns AsyncPage instead of Page
# isinstance checks work cleanlyassert isinstance(page, AsyncPage)AsyncPage is a subclass of Page and has the same navigation API (has_next(), next_page_number(), etc.).