LimitOffsetPagination: Effective limit when PAGE_SIZE is undefined but MAX_PAGE_SIZE is set
0 reputation · 22 Feb 2021, 23:31 UTC
0 reputation · 22 Feb 2021, 23:31 UTC
When configuring Django REST Framework's LimitOffsetPagination, the PAGE_SIZE setting defines the default page size, while PAGINATE_BY_MAX (or MAX_PAGE_SIZE) establishes an upper bound for client‑requested limits. If PAGE_SIZE is left undefined, the framework falls back to its internal default limit value. It is unclear whether this internal default is also subject to the MAX_PAGE_SIZE ceiling, or whether the limit remains unbounded unless a client explicitly supplies a limit parameter.
The goal is to clarify the exact limit applied in three scenarios: (1) a request with no limit query parameter, (2) a request with a limit below the MAX_PAGE_SIZE threshold, and (3) a request with a limit exceeding that threshold. Understanding this interaction helps prevent accidental over‑fetching and ensures consistent API behavior across deployments.
What limit does LimitOffsetPagination use when PAGE_SIZE is not set and no limit query parameter is supplied? Does the MAX_PAGE_SIZE setting apply to that internal default limit? How does the pagination class treat a client‑provided limit that exceeds the configured MAX_PAGE_SIZE?
35775 reputation · 23 Feb 2021, 00:06 UTC
Short answer: when PAGE_SIZE is not set, LimitOffsetPagination falls back to its internal default_limit of 100, and that fallback is not capped by MAX_PAGE_SIZE. The ceiling only applies to client-supplied limit query parameters. This is well-established behavior in DRF 3.x, visible in rest_framework/pagination.py.
limit query parameter: the effective limit is default_limit — 100 unless you set PAGE_SIZE or override the attribute. MAX_PAGE_SIZE is ignored here, even if it is lower than 100.MAX_PAGE_SIZE: the client's value is used as-is.MAX_PAGE_SIZE: the limit is clamped. get_limit() effectively applies min(limit, max_limit), so the response contains at most MAX_PAGE_SIZE rows.The two settings map to independent attributes on the pagination class: PAGE_SIZE → default_limit, and MAX_PAGE_SIZE → max_limit. The clamping logic runs only inside the branch that parses the query parameter; the no-parameter path returns default_limit directly without comparing it to max_limit. Note that PageNumberPagination behaves differently — its page_size interacts with max_page_size — so do not assume symmetry across pagination classes.
If you configured MAX_PAGE_SIZE = 50 expecting a global ceiling, you have a gap: unparameterized requests still return up to 100 rows. Fix it by setting an explicit page size at or below the ceiling:
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination',
'PAGE_SIZE': 50,
}
class MyPagination(LimitOffsetPagination):
max_limit = 50
Or override default_limit directly on a custom subclass.
Confirm on your installed version with a quick test: set MAX_PAGE_SIZE = 50, leave PAGE_SIZE unset, and request the endpoint three ways — no limit, ?limit=25, ?limit=100. Expected counts: 100, 25, 50. You can also inspect the instance directly:
p = MyPagination()
print(p.default_limit, p.max_limit)
One caveat: custom pagination classes in your project (or third-party packages) may override get_limit(), so check for subclasses before assuming stock behavior. The behavior described here applies to stock DRF 3.x; if you are on a much older or newer major version, re-run the test above rather than trusting the constant.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.