Skip to content

Add MAX_PAGE_SIZE setting#9107

Open
TheSuperiorStanislav wants to merge 6 commits intoencode:mainfrom
TheSuperiorStanislav:add-max-page-setting-for-pagination
Open

Add MAX_PAGE_SIZE setting#9107
TheSuperiorStanislav wants to merge 6 commits intoencode:mainfrom
TheSuperiorStanislav:add-max-page-setting-for-pagination

Conversation

@TheSuperiorStanislav
Copy link
Copy Markdown
Contributor

@TheSuperiorStanislav TheSuperiorStanislav commented Sep 14, 2023

Description

Add a setting for max page size for global pagination. Our team discovered that by default, page size is unlimited which could lead to API abuse and the only way to fix it -> it's to subclass pagination class(which is a working solution, but not convenient). After investigating issues I found related issue and comment.

@auvipy auvipy self-requested a review September 14, 2023 14:11
Copy link
Copy Markdown
Collaborator

@auvipy auvipy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this deserve a test case,, but as it is adding/exposing new API surface to the framework, we might need to pass the design decision step first

@auvipy
Copy link
Copy Markdown
Collaborator

auvipy commented Sep 14, 2023

OK I saw the comment now #6185 (comment) it might be possible in class level but not globally at the moment.

@auvipy auvipy added this to the 3.15 milestone Sep 14, 2023
@TheSuperiorStanislav
Copy link
Copy Markdown
Contributor Author

@auvipy I added tests for system checks, but didn't managed to find and add tests for settings with API requests like in test_pagination.py, and it's kind hard to add since pagination settings are defined as class attrs.

@auvipy
Copy link
Copy Markdown
Collaborator

auvipy commented Oct 2, 2023

we got a PR related to this area #8993. would be helpful if you review and share your views on the PR.

@ankitchhatbar
Copy link
Copy Markdown

ankitchhatbar commented Dec 22, 2023

@auvipy @christophehenry @TheSuperiorStanislav If you don't mind, I can take this up and implement the changes requested

@TheSuperiorStanislav
Copy link
Copy Markdown
Contributor Author

TheSuperiorStanislav commented Aug 21, 2024

@auvipy @christophehenry Could you please take a look? Sorry for taking that long, btw 🥲

Comment on lines +191 to +196
def page_size(self) -> int:
"""Get default page size.

Defaults to `None`, meaning pagination is disabled.

"""
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There shoud probably be a comment explaining why this i a property to avoid removing it in a future refactor?

@christophehenry
Copy link
Copy Markdown

christophehenry commented Aug 21, 2024

Looks good to me. Thank you.

@TheSuperiorStanislav
Copy link
Copy Markdown
Contributor Author

@auvipy Could you take a look, please? This pr has been here for a while)

limit_query_description = _('Number of results to return per page.')
offset_query_param = 'offset'
offset_query_description = _('The initial index from which to return the results.')
max_limit = None
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we avoid changing the style here?

max_limit = api_settings.MAX_PAGE_SIZE

Perfer minimal impact footprints for PRs.

Copy link
Copy Markdown
Contributor Author

@TheSuperiorStanislav TheSuperiorStanislav Sep 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's related to #8993 and #9107 (comment). Without it we can't properly test it, since this setting will be set during init.

Copy link
Copy Markdown
Contributor

@lovelydinosaur lovelydinosaur Sep 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hrm... not sure? If you'd like to take that approach then I'd probably suggest handling #8993 separately, as a precursor to this.
Unclear to me why that'd be different to attributes we use in other cases.

@stale
Copy link
Copy Markdown

stale bot commented Apr 26, 2025

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@stale stale bot added the stale label Apr 26, 2025
@browniebroke browniebroke removed this from the 3.15 milestone Jul 6, 2025
@stale stale bot removed the stale label Jul 6, 2025
@deepakangadi
Copy link
Copy Markdown
Contributor

2000 lines and 80 chars per line.

@auvipy auvipy requested review from Copilot April 2, 2026 14:29
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a global MAX_PAGE_SIZE REST framework setting to cap client-requested pagination sizes (avoiding unbounded page sizes without requiring custom pagination subclasses), and integrates it into built-in pagination classes, with docs and tests updates.

Changes:

  • Add MAX_PAGE_SIZE to REST_FRAMEWORK default settings and extend the pagination system check to account for it.
  • Read MAX_PAGE_SIZE from api_settings in built-in paginators (PageNumber/LimitOffset/Cursor) via new properties.
  • Add/extend tests and documentation to cover the new setting and warning behavior.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 12 comments.

Show a summary per file
File Description
rest_framework/settings.py Introduces MAX_PAGE_SIZE default setting.
rest_framework/pagination.py Wires MAX_PAGE_SIZE into pagination implementations via settings-backed properties.
rest_framework/checks.py Updates the pagination system check to warn when MAX_PAGE_SIZE is set without a default pagination class.
tests/test_settings.py Extends system-check integration test to include MAX_PAGE_SIZE.
tests/test_pagination.py Adds integration tests validating settings-based max page size behavior.
docs/api-guide/pagination.md Documents global configuration including MAX_PAGE_SIZE.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 595 to 597
def paginate_queryset(self, queryset, request, view=None):
self.request = request
self.page_size = self.get_page_size(request)
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CursorPagination.page_size is now a read-only @Property, but paginate_queryset() assigns to self.page_size. That assignment will raise AttributeError at runtime. Consider either keeping page_size as a regular attribute, or adding a setter/backing attribute so paginate_queryset can cache the computed per-request page size safely.

Copilot uses AI. Check for mistakes.
invalid_page_message = _('Invalid page.')

@property
def page_size(self) -> int:
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The return type annotation here should allow None. api_settings.PAGE_SIZE defaults to None (pagination disabled), so annotating this as int is inaccurate and can confuse type checkers; consider using Optional[int]/int | None (or dropping the annotation to match the rest of the module).

Copilot uses AI. Check for mistakes.
return api_settings.PAGE_SIZE

@property
def max_page_size(self) -> int:
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same type issue as page_size: api_settings.MAX_PAGE_SIZE defaults to None, so this property's return type should be Optional[int]/int | None rather than int (or omit the annotation for consistency).

Copilot uses AI. Check for mistakes.
Set to an integer to limit the maximum page size the client may request.
Only relevant if 'page_size_query_param' has also been set.
Defaults to `None`, meaning page size is unlimited.
It's recommended that you would set a limit to avoid api abuse.
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Docstring grammar/casing: “It's recommended that you would…” is ungrammatical, and “api” should be “API”. Consider rephrasing to “It's recommended that you set a limit to avoid API abuse.”

Suggested change
It's recommended that you would set a limit to avoid api abuse.
It's recommended that you set a limit to avoid API abuse.

Copilot uses AI. Check for mistakes.
Comment on lines +365 to +368
Set to an integer to limit the maximum page size the client may request.
Only relevant if 'page_size_query_param' has also been set.
Defaults to `None`, meaning page size is unlimited.
It's recommended that you would set a limit to avoid api abuse.
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LimitOffsetPagination uses limit_query_param, not page_size_query_param, so this docstring is misleading. Also the same grammar/casing issue applies (“API abuse”). Update the text to refer to limit_query_param (and/or max_limit) and fix the wording.

Suggested change
Set to an integer to limit the maximum page size the client may request.
Only relevant if 'page_size_query_param' has also been set.
Defaults to `None`, meaning page size is unlimited.
It's recommended that you would set a limit to avoid api abuse.
Set to an integer to limit the maximum number of results the client may
request via the ``limit_query_param``.
Defaults to ``None``, meaning the limit is unrestricted.
It is recommended that you set a limit to avoid API abuse.

Copilot uses AI. Check for mistakes.
Comment on lines +35 to +36
Note that you need to set both the pagination class, and the page size and limit that should be used.
`DEFAULT_PAGINATION_CLASS`, `PAGE_SIZE`, `MAX_PAGE_SIZE` are `None` by default.
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This note is a bit too strong/inaccurate: you don’t always need to set both PAGE_SIZE and MAX_PAGE_SIZE for pagination to work (e.g., LimitOffsetPagination can rely on the client-provided limit, and MAX_PAGE_SIZE only matters when a client-controllable size param is enabled). Consider rewording to clarify which settings are required vs optional depending on the pagination class.

Copilot uses AI. Check for mistakes.
Comment on lines +162 to +167
@override_settings(
REST_FRAMEWORK={
"MAX_PAGE_SIZE": 20,
"PAGE_SIZE": 5,
}
)
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test block switches to double quotes for strings (e.g., REST_FRAMEWORK keys and response.data indexing) while the surrounding tests in this module predominantly use single quotes. Consider using the existing quote style here for consistency and to reduce diff noise.

Copilot uses AI. Check for mistakes.
return api_settings.PAGE_SIZE

@property
def max_page_size(self) -> int:
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These settings-backed properties can return None (MAX_PAGE_SIZE defaults to None), but the return annotation is int. Consider changing to Optional[int]/int | None (or removing the annotation) to avoid misleading type information.

Copilot uses AI. Check for mistakes.
template = 'rest_framework/pagination/numbers.html'

@property
def max_limit(self) -> int:
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same typing concern as elsewhere: api_settings.MAX_PAGE_SIZE (and PAGE_SIZE) default to None, so annotating these as int is inaccurate. Adjust the annotations to allow None or omit them for consistency with the rest of the module.

Copilot uses AI. Check for mistakes.
It's recommended that you would set a limit to avoid api abuse.

"""
return api_settings.MAX_PAGE_SIZE
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MAX_PAGE_SIZE is now wired into LimitOffsetPagination via max_limit (and also into CursorPagination/PageNumberPagination). There are new integration tests for PageNumberPagination settings, but no corresponding tests that MAX_PAGE_SIZE actually caps limit for LimitOffsetPagination (and/or caps cursor page size when enabled). Adding a couple of targeted tests would help prevent regressions across pagination styles.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants