Skip to content

Commit b17dcec

Browse files
authored
Merge pull request #1657
Cursor pagination #1584
2 parents 54bd159 + 391d362 commit b17dcec

4 files changed

Lines changed: 1437 additions & 3 deletions

File tree

docs/docs/guides/response/pagination.md

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,67 @@ Example query:
8888

8989
This allows you to temporarily override the page size setting in your request. The request will use the specified `page_size` value if provided. Otherwise, it will use either the value specified in the decorator or the value from `PAGINATION_MAX_PER_PAGE_SIZE` in settings.py if no decorator value is set.
9090

91+
### CursorPagination
92+
93+
Cursor-based pagination provides stable pagination for datasets that may change frequently. Cursor pagination uses base64 encoded tokens to mark positions in the dataset, ensuring consistent results even when items are added or removed.
94+
95+
```python hl_lines="1 4"
96+
from ninja.pagination import paginate, CursorPagination
97+
98+
@api.get('/events', response=List[EventSchema])
99+
@paginate(CursorPagination)
100+
def list_events(request):
101+
return Event.objects.all()
102+
```
103+
104+
Example query:
105+
106+
```
107+
/api/events?cursor=eyJwIjoiMjAyNC0wMS0wMSIsInIiOmZhbHNlLCJvIjowfQ==
108+
```
109+
110+
this class has two input parameters:
111+
112+
- `cursor` - base64 token representing the current position (optional, starts from beginning if not provided)
113+
- `page_size` - number of items per page (optional)
114+
115+
You can specify the `page_size` value to temporarily override in the request:
116+
117+
```
118+
/api/events?cursor=eyJwIjoiMjAyNC0wMS0wMSIsInIiOmZhbHNlLCJvIjowfQ==&page_size=5
119+
```
120+
121+
This class has a few parameters, which determine how the cursor position is ascertained and the parameter encoded:
122+
123+
- `ordering` - tuple of field names to order the queryset. Use `-` prefix for descending order. The first one of which will be used to encode the position. The ordering field should be unique if possible. A string representation of this field will be used to point to the current position of the cursor. Timestamps work well if each item in the collection is created independently. The paginator can handle some non-uniqueness by adding an offset. Defaults to `("-pk",)`, change in `NINJA_PAGINATION_DEFAULT_ORDERING`
124+
125+
- `page_size` - default page size for endpoint. Defaults to `100`, change in `NINJA_PAGINATION_PER_PAGE`
126+
- `max_page_size` - maximum allowed page size for endpoint. Defaults to `100`, change in `NINJA_PAGINATION_MAX_PER_PAGE_SIZE`
127+
128+
Finally, there is a `NINJA_PAGINATION_MAX_OFFSET` setting to limit malicious cursor requests. It defaults to `100`.
129+
130+
The class parameters can be set globally via settings as well as per view:
131+
132+
```python hl_lines="2"
133+
@api.get("/events")
134+
@paginate(CursorPagination, ordering=("start_date", "end_date"), page_size=20, max_page_size=100)
135+
def list_events(request):
136+
return Event.objects.all()
137+
```
138+
139+
The response includes navigation links and results:
140+
141+
```json
142+
{
143+
"next": "http://api.example.com/events?cursor=eyJwIjoiMjAyNC0wMS0wMiIsInIiOmZhbHNlLCJvIjowfQ==",
144+
"previous": "http://api.example.com/events?cursor=eyJwIjoiMjAyNC0wMS0wMSIsInIiOnRydWUsIm8iOjB9",
145+
"results": [
146+
{ "id": 1, "title": "Event 1", "start_date": "2024-01-01" },
147+
{ "id": 2, "title": "Event 2", "start_date": "2024-01-02" }
148+
]
149+
}
150+
```
151+
91152
## Accessing paginator parameters in view function
92153

93154
If you need access to `Input` parameters used for pagination in your view function - use `pass_parameter` argument

ninja/conf.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from math import inf
2-
from typing import Dict, Optional, Set
2+
from typing import Dict, Optional, Set, Tuple
33

44
from django.conf import settings as django_settings
55
from pydantic import BaseModel, ConfigDict, Field
@@ -11,6 +11,10 @@ class Settings(BaseModel):
1111
PAGINATION_CLASS: str = Field(
1212
"ninja.pagination.LimitOffsetPagination", alias="NINJA_PAGINATION_CLASS"
1313
)
14+
PAGINATION_DEFAULT_ORDERING: Tuple[str, ...] = Field(
15+
("-pk",), alias="NINJA_PAGINATION_DEFAULT_ORDERING"
16+
)
17+
PAGINATION_MAX_OFFSET: int = Field(100, alias="NINJA_PAGINATION_MAX_OFFSET")
1418
PAGINATION_PER_PAGE: int = Field(100, alias="NINJA_PAGINATION_PER_PAGE")
1519
PAGINATION_MAX_PER_PAGE_SIZE: int = Field(100, alias="NINJA_MAX_PER_PAGE_SIZE")
1620
PAGINATION_MAX_LIMIT: int = Field(inf, alias="NINJA_PAGINATION_MAX_LIMIT") # type: ignore

0 commit comments

Comments
 (0)