Skip to content

Commit

Permalink
Add async analog of default paginate (#1039)
Browse files Browse the repository at this point in the history
  • Loading branch information
uriyyo authored Feb 22, 2024
1 parent 7565c9a commit 49c3f4d
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 0 deletions.
36 changes: 36 additions & 0 deletions fastapi_pagination/async_paginator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
from typing import Any, Callable, Optional, Sequence, TypeVar

__all__ = ["paginate"]

from .api import apply_items_transformer, create_page
from .bases import AbstractParams
from .types import AdditionalData, SyncItemsTransformer
from .utils import check_installed_extensions, verify_params

T = TypeVar("T")


# same as default paginator, but allow to use async transformer
async def paginate(
sequence: Sequence[T],
params: Optional[AbstractParams] = None,
length_function: Callable[[Sequence[T]], int] = len,
*,
safe: bool = False,
transformer: Optional[SyncItemsTransformer] = None,
additional_data: Optional[AdditionalData] = None,
) -> Any:
if not safe:
check_installed_extensions()

params, raw_params = verify_params(params, "limit-offset")

items = sequence[raw_params.as_slice()]
t_items = await apply_items_transformer(items, transformer, async_=True)

return create_page(
t_items,
total=length_function(sequence) if raw_params.include_total else None,
params=params,
**(additional_data or {}),
)
27 changes: 27 additions & 0 deletions tests/test_async_paginator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
from fastapi import FastAPI
from pytest import fixture

from fastapi_pagination import (
LimitOffsetPage,
Page,
add_pagination,
)
from fastapi_pagination.async_paginator import paginate

from .base import BasePaginationTestCase
from .utils import OptionalLimitOffsetPage, OptionalPage


class TestAsyncPaginationParams(BasePaginationTestCase):
@fixture(scope="session")
def app(self, model_cls, entities):
app = FastAPI()

@app.get("/default", response_model=Page[model_cls])
@app.get("/limit-offset", response_model=LimitOffsetPage[model_cls])
@app.get("/optional/default", response_model=OptionalPage[model_cls])
@app.get("/optional/limit-offset", response_model=OptionalLimitOffsetPage[model_cls])
async def route():
return await paginate(entities)

return add_pagination(app)

2 comments on commit 49c3f4d

@SnoozeFreddo
Copy link

Choose a reason for hiding this comment

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

Hey there,

what's the difference between fastapi_pagination.async_paginator and fastapi_pagination.ext.sqlalchemy.paginator ?

Can i somehow use async with the sqlalchemy paginator?

@uriyyo
Copy link
Owner Author

@uriyyo uriyyo commented on 49c3f4d Feb 22, 2024

Choose a reason for hiding this comment

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

Hi @SnoozeFreddo

what's the difference between fastapi_pagination.async_paginator and fastapi_pagination.ext.sqlalchemy.paginator ?

The idea of async_paginator is to be able to use async transformers, you can use only sync transformers with default paginate but in some cases, you want to paginate data that comes from an external source and then transform it, #950 is a good example of this use case.
astapi_pagination.ext.sqlalchemy.paginator is used to work with sqlalchemy queries and paginate/async_paginate is used to work with python sequences (list/tuple/.etc).

Can i somehow use async with the sqlalchemy paginator?

sqlalchemy extension already supports async drivers, you can take a look at the example - https://github.com/uriyyo/fastapi-pagination/blob/main/examples/pagination_async_sqlalchemy.py

Please sign in to comment.