DRF cho 3 style pagination, mỗi cái tối ưu cho một kiểu UI/dataset khác nhau.
PageNumberPagination (?page=2&page_size=20) hiển thị "Page 1 / 2 / 3 ..." — hợp với UI có số trang. Đơn giản, nhưng OFFSET càng lớn càng chậm. LimitOffsetPagination (?limit=20&offset=40) linh hoạt cho client tự quản, gặp cùng vấn đề OFFSET chậm trên bảng to. CursorPagination (?cursor=cD0yMDI2...) dùng cột sắp xếp (-created_at) làm cursor, nên mọi trang đều O(log n) — không có "page number" và "tổng trang" nhưng cực hợp infinite scroll, feed real-time.
class PostCursorPagination(CursorPagination):
page_size = 20
ordering = '-created_at'
cursor_query_param = 'cursor'
class PostViewSet(viewsets.ModelViewSet):
pagination_class = PostCursorPaginationBảng quá 1M row mà dùng page-number pagination kèm count() mỗi request là một thảm hoạ — COUNT(*) quét toàn bảng, response chậm vài giây.
Cách giải: hoặc override paginator.get_paginated_response để bỏ count, hoặc đổi sang cursor luôn.
DRF ships three styles, each optimized for a UI/dataset shape:
PageNumberPagination—?page=2&page_size=20. Renders "Page 1 / 2 / 3 ...". Fits classic numbered UIs. Simple, but largeOFFSETslows down.LimitOffsetPagination—?limit=20&offset=40. Flexible client-driven control. SameOFFSETslowness on big tables.CursorPagination—?cursor=cD0yMDI2.... Uses the sort column (-created_at) as the cursor → every page is O(log n). No page numbers / total count; fits infinite scroll and real-time feeds.
class PostCursorPagination(CursorPagination):
page_size = 20
ordering = '-created_at'
cursor_query_param = 'cursor'
class PostViewSet(viewsets.ModelViewSet):
pagination_class = PostCursorPaginationPitfall: A 1M+ row table with page-number pagination and count() on every request → COUNT(*) scans the whole table = brutally slow.
Either override paginator.get_paginated_response to drop count, or switch to cursor.