Django · Pagination
Django Pagination: Order Rows and Choose an Invalid-Page Policy
Give paginated archives an explicit order, deliberate invalid-page handling and navigation that preserves active filters.

An article archive needs more than a limit on the number of rows. Readers also need a predictable sequence and a clear result when a page parameter is invalid.
Order before dividing
For an illustrative Article model, start with published rows and an explicit order:
from django.core.paginator import Paginator
articles = Article.objects.filter(is_published=True).order_by(
"-published_at", "-pk"
)
page_obj = Paginator(articles, 12).get_page(request.GET.get("page"))
The primary key breaks ties when articles share a publication date. This makes the ordering unambiguous for a fixed dataset; inserts and deletions can still shift records between page-number requests.
Choose the invalid-input policy
Django's get_page() returns the first page for nonnumeric input and the last page for negative or excessively high numbers. That forgiving behavior suits many browser archives. Use page() and handle its exceptions when the interface requires an explicit invalid-page response instead.
Keep navigation tied to the page
Render page_obj and show next or previous links only when the corresponding has_next() or has_previous() check succeeds. Preserve active search and topic parameters when constructing those links so moving forward does not reset the reader's filters.
Check realistic boundaries
Try an empty archive, tied publication dates, the final partial page and malformed page input. For a large archive, measure high page numbers: database OFFSET work can grow even though each response displays only twelve records.