Skip to content

Commit

Permalink
fix: async pagination evaluate fix for queryset object
Browse files Browse the repository at this point in the history
  • Loading branch information
WellingtonNico committed Dec 7, 2024
1 parent ed41ef8 commit 69fadf2
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 30 deletions.
1 change: 1 addition & 0 deletions ninja/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ class Settings(BaseModel):
"ninja.pagination.LimitOffsetPagination", alias="NINJA_PAGINATION_CLASS"
)
PAGINATION_PER_PAGE: int = Field(100, alias="NINJA_PAGINATION_PER_PAGE")
PAGINATION_MAX_PER_PAGE: int = Field(100, alias="NINJA_PAGINATION_MAX_PER_PAGE")
PAGINATION_MAX_LIMIT: int = Field(inf, alias="NINJA_PAGINATION_MAX_LIMIT") # type: ignore

# Throttling
Expand Down
29 changes: 17 additions & 12 deletions ninja/pagination.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from math import inf
from typing import Any, AsyncGenerator, Callable, List, Optional, Tuple, Type, Union

from asgiref.sync import sync_to_async
from django.db.models import QuerySet
from django.http import HttpRequest
from django.utils.module_loading import import_string
Expand Down Expand Up @@ -80,9 +81,11 @@ class Input(Schema):
limit: int = Field(
settings.PAGINATION_PER_PAGE,
ge=1,
le=settings.PAGINATION_MAX_LIMIT
if settings.PAGINATION_MAX_LIMIT != inf
else None,
le=(
settings.PAGINATION_MAX_LIMIT
if settings.PAGINATION_MAX_LIMIT != inf
else None
),
)
offset: int = Field(0, ge=0)

Expand All @@ -108,19 +111,19 @@ async def apaginate_queryset(
offset = pagination.offset
limit: int = min(pagination.limit, settings.PAGINATION_MAX_LIMIT)
return {
"items": queryset[offset : offset + limit],
"items": await sync_to_async(list)(queryset[offset : offset + limit]),
"count": await self._aitems_count(queryset),
} # noqa: E203


class PageNumberPagination(AsyncPaginationBase):
class Input(Schema):
page: int = Field(1, ge=1)
page_size: int = Field(
settings.PAGINATION_PER_PAGE, ge=1, le=settings.PAGINATION_MAX_PER_PAGE
)

def __init__(
self, page_size: int = settings.PAGINATION_PER_PAGE, **kwargs: Any
) -> None:
self.page_size = page_size
def __init__(self, **kwargs: Any) -> None:
super().__init__(**kwargs)

def paginate_queryset(
Expand All @@ -129,9 +132,9 @@ def paginate_queryset(
pagination: Input,
**params: Any,
) -> Any:
offset = (pagination.page - 1) * self.page_size
offset = (pagination.page - 1) * pagination.page_size
return {
"items": queryset[offset : offset + self.page_size],
"items": queryset[offset : offset + pagination.page_size],
"count": self._items_count(queryset),
} # noqa: E203

Expand All @@ -141,9 +144,11 @@ async def apaginate_queryset(
pagination: Input,
**params: Any,
) -> Any:
offset = (pagination.page - 1) * self.page_size
offset = (pagination.page - 1) * pagination.page_size
return {
"items": queryset[offset : offset + self.page_size],
"items": await sync_to_async(list)(
queryset[offset : offset + pagination.page_size]
),
"count": await self._aitems_count(queryset),
} # noqa: E203

Expand Down
63 changes: 50 additions & 13 deletions tests/test_pagination.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,19 @@

import pytest
from django.test import override_settings
from pydantic.errors import PydanticSchemaGenerationError

from ninja import NinjaAPI, Schema
from ninja.errors import ConfigError
from ninja.operation import Operation
from ninja.pagination import (
LimitOffsetPagination,
PageNumberPagination,
PaginationBase,
make_response_paginated,
PageNumberPagination,
paginate,
PaginationBase,
)
from ninja.testing import TestClient
from pydantic.errors import PydanticSchemaGenerationError

api = NinjaAPI()

Expand Down Expand Up @@ -119,19 +119,19 @@ def items_3(request, **kwargs):


@api.get("/items_4", response=List[int])
@paginate(PageNumberPagination, page_size=10)
@paginate(PageNumberPagination)
def items_4(request, **kwargs):
return ITEMS


@api.get("/items_5", response=List[int])
@paginate(PageNumberPagination, page_size=10)
@paginate(PageNumberPagination)
def items_5(request):
return ITEMS


@api.get("/items_6", response={101: int, 200: List[Any]})
@paginate(PageNumberPagination, page_size=10, pass_parameter="page_info")
@paginate(PageNumberPagination, pass_parameter="page_info")
def items_6(request, **kwargs):
return ITEMS + [kwargs["page_info"]]

Expand Down Expand Up @@ -244,7 +244,7 @@ def test_case3():


def test_case4():
response = client.get("/items_4?page=2").json()
response = client.get("/items_4?page=2&page_size=10").json()
assert response == {"items": ITEMS[10:20], "count": 100}

schema = api.get_openapi_schema()["paths"]["/api/items_4"]["get"]
Expand All @@ -260,12 +260,24 @@ def test_case4():
"type": "integer",
},
"required": False,
}
},
{
"in": "query",
"name": "page_size",
"schema": {
"default": 100,
"maximum": 100,
"minimum": 1,
"title": "Page Size",
"type": "integer",
},
"required": False,
},
]


def test_case5_no_kwargs():
response = client.get("/items_5?page=2").json()
response = client.get("/items_5?page=2&page_size=10").json()
assert response == {"items": ITEMS[10:20], "count": 100}

schema = api.get_openapi_schema()["paths"]["/api/items_5"]["get"]
Expand All @@ -281,14 +293,27 @@ def test_case5_no_kwargs():
"type": "integer",
},
"required": False,
}
},
{
"in": "query",
"name": "page_size",
"schema": {
"default": 100,
"maximum": 100,
"minimum": 1,
"title": "Page Size",
"type": "integer",
},
"required": False,
},
]


def test_case6_pass_param_kwargs():
page = 11
response = client.get(f"/items_6?page={page}").json()
assert response == {"items": [{"page": 11}], "count": 101}
page_size = 10
response = client.get(f"/items_6?page={page}&page_size={page_size}").json()
assert response == {"items": [{"page": page, "page_size": page_size}], "count": 101}

schema = api.get_openapi_schema()["paths"]["/api/items_6"]["get"]

Expand All @@ -303,7 +328,19 @@ def test_case6_pass_param_kwargs():
"type": "integer",
},
"required": False,
}
},
{
"in": "query",
"name": "page_size",
"schema": {
"default": 100,
"maximum": 100,
"minimum": 1,
"title": "Page Size",
"type": "integer",
},
"required": False,
},
]


Expand Down
14 changes: 9 additions & 5 deletions tests/test_pagination_async.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
from ninja.pagination import (
AsyncPaginationBase,
PageNumberPagination,
PaginationBase,
paginate,
PaginationBase,
)
from ninja.testing import TestAsyncClient

Expand Down Expand Up @@ -113,11 +113,15 @@ async def test_async_page_number():
api = NinjaAPI()

@api.get("/items_page_number", response=List[Any])
@paginate(PageNumberPagination, page_size=10, pass_parameter="page_info")
@paginate(PageNumberPagination, pass_parameter="page_info")
async def items_page_number(request, **kwargs):
return ITEMS + [kwargs["page_info"]]

client = TestAsyncClient(api)

response = await client.get("/items_page_number?page=11")
assert response.json() == {"items": [{"page": 11}], "count": 101}
page = 11
page_size = 10
response = await client.get(f"/items_page_number?page={page}&page_size={page_size}")
assert response.json() == {
"items": [{"page": page, "page_size": page_size}],
"count": 101,
}

0 comments on commit 69fadf2

Please sign in to comment.