paginate-fastapi 0.1.0__py3-none-any.whl → 1.0.2__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- {paginate_fastapi-0.1.0.dist-info → paginate_fastapi-1.0.2.dist-info}/METADATA +16 -1
- paginate_fastapi-1.0.2.dist-info/RECORD +8 -0
- pagination/__init__.py +11 -1
- pagination/decorator.py +117 -0
- pagination/middleware.py +0 -2
- pagination/models.py +7 -11
- paginate_fastapi-0.1.0.dist-info/RECORD +0 -7
- {paginate_fastapi-0.1.0.dist-info → paginate_fastapi-1.0.2.dist-info}/LICENSE +0 -0
- {paginate_fastapi-0.1.0.dist-info → paginate_fastapi-1.0.2.dist-info}/WHEEL +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: paginate-fastapi
|
3
|
-
Version:
|
3
|
+
Version: 1.0.2
|
4
4
|
Summary: A simple and efficient pagination library for FastAPI applications
|
5
5
|
License: MIT
|
6
6
|
Keywords: fastapi,sqlmodel,pagination,async,filtering,sorting
|
@@ -67,6 +67,21 @@ pip install paginate-fastapi
|
|
67
67
|
|
68
68
|
## Quick Start
|
69
69
|
|
70
|
+
### Usage as decorator
|
71
|
+
```python
|
72
|
+
from pagination.decorator import paginate
|
73
|
+
|
74
|
+
@router.get("/", response_model=PageResponse[YourModel])
|
75
|
+
@paginate(YourModel, lambda: get_db)
|
76
|
+
async def get_items(
|
77
|
+
db: AsyncSession = Depends(get_db),
|
78
|
+
pagination: PaginationParams = Depends()
|
79
|
+
):
|
80
|
+
...
|
81
|
+
return { 'extra_data': 'data' }
|
82
|
+
```
|
83
|
+
|
84
|
+
### Usage as middleware
|
70
85
|
```python
|
71
86
|
from fastapi import FastAPI, Depends
|
72
87
|
from sqlmodel import SQLModel, Field
|
@@ -0,0 +1,8 @@
|
|
1
|
+
pagination/__init__.py,sha256=YzRh-vflsV-bTqz2Kh-uNYQEkZYeSYoX3k9znzdscas,1076
|
2
|
+
pagination/decorator.py,sha256=t0fjp-P6cJV05fevxmoceOyZLanR0R2mPKtblVFMHnk,4070
|
3
|
+
pagination/middleware.py,sha256=Y2AuLvmrG6nuj_BtwmuzDQdyQphEDy9gaag8oGsgVv0,6131
|
4
|
+
pagination/models.py,sha256=p01gxJlj-yDeYbLCe4Zm5M-WXh2sYJdI7XYlBfWcp6k,3387
|
5
|
+
paginate_fastapi-1.0.2.dist-info/LICENSE,sha256=04BsGfNMsEqA705-GhDsFirDIQyWUiCWJFx0h2ss6yE,1079
|
6
|
+
paginate_fastapi-1.0.2.dist-info/METADATA,sha256=7OKL7meGnK05T7CujCiBksQpveuGOhyGykLXX5HL4Bc,5739
|
7
|
+
paginate_fastapi-1.0.2.dist-info/WHEEL,sha256=XbeZDeTWKc1w7CSIyre5aMDU_-PohRwTQceYnisIYYY,88
|
8
|
+
paginate_fastapi-1.0.2.dist-info/RECORD,,
|
pagination/__init__.py
CHANGED
@@ -6,9 +6,11 @@ to FastAPI applications using SQLModel.
|
|
6
6
|
|
7
7
|
Example:
|
8
8
|
from fastapi import FastAPI, Depends
|
9
|
-
from pagination import PaginationMiddleware, PaginationParams
|
9
|
+
from pagination import PaginationMiddleware, PaginationParams, paginate
|
10
10
|
|
11
11
|
app = FastAPI()
|
12
|
+
|
13
|
+
# Using middleware
|
12
14
|
paginator = PaginationMiddleware(get_session)
|
13
15
|
|
14
16
|
@app.get("/items/")
|
@@ -17,8 +19,15 @@ Example:
|
|
17
19
|
paginator: PaginationMiddleware = Depends(lambda: paginator),
|
18
20
|
):
|
19
21
|
return await paginator.paginate(Item, pagination)
|
22
|
+
|
23
|
+
# Using decorator
|
24
|
+
@app.get("/users/")
|
25
|
+
@paginate(User, get_session)
|
26
|
+
async def get_users():
|
27
|
+
return {"extra": "Additional data can be included"}
|
20
28
|
"""
|
21
29
|
|
30
|
+
from .decorator import paginate
|
22
31
|
from .middleware import PaginationMiddleware
|
23
32
|
from .models import FilterOperator, PageResponse, PaginationParams, SortOrder
|
24
33
|
|
@@ -28,4 +37,5 @@ __all__ = [
|
|
28
37
|
"PaginationMiddleware",
|
29
38
|
"PaginationParams",
|
30
39
|
"SortOrder",
|
40
|
+
"paginate",
|
31
41
|
]
|
pagination/decorator.py
ADDED
@@ -0,0 +1,117 @@
|
|
1
|
+
"""
|
2
|
+
Pagination utilities.
|
3
|
+
"""
|
4
|
+
|
5
|
+
from collections.abc import Callable
|
6
|
+
|
7
|
+
from fastapi import Depends
|
8
|
+
from sqlalchemy import func
|
9
|
+
from sqlalchemy.ext.asyncio import AsyncSession
|
10
|
+
from sqlmodel import SQLModel, select
|
11
|
+
|
12
|
+
from pagination.models import PageResponse, PaginationParams
|
13
|
+
|
14
|
+
|
15
|
+
def paginate(model: type[SQLModel], db_dependency: Callable):
|
16
|
+
"""
|
17
|
+
Decorator for FastAPI endpoints to add pagination support.
|
18
|
+
|
19
|
+
Usage:
|
20
|
+
@router.get("/", response_model=PageResponse[YourModel])
|
21
|
+
@paginate(YourModel, lambda: get_db)
|
22
|
+
async def get_items(
|
23
|
+
db: AsyncSession = Depends(get_db),
|
24
|
+
pagination: PaginationParams = Depends()
|
25
|
+
):
|
26
|
+
pass # The decorator handles everything
|
27
|
+
"""
|
28
|
+
|
29
|
+
def decorator(function: Callable) -> Callable:
|
30
|
+
def _apply_filter(query, field, operator: str, value):
|
31
|
+
"""Apply filter operation to query."""
|
32
|
+
filter_ops = {
|
33
|
+
"eq": lambda: field == value,
|
34
|
+
"neq": lambda: field != value,
|
35
|
+
"gt": lambda: field > value,
|
36
|
+
"lt": lambda: field < value,
|
37
|
+
"gte": lambda: field >= value,
|
38
|
+
"lte": lambda: field <= value,
|
39
|
+
"in": lambda: field.in_(value),
|
40
|
+
"not_in": lambda: field.not_in(value),
|
41
|
+
"contains": lambda: field.contains(value),
|
42
|
+
}
|
43
|
+
if op_func := filter_ops.get(operator):
|
44
|
+
return query.where(op_func())
|
45
|
+
return query
|
46
|
+
|
47
|
+
async def wrapper(
|
48
|
+
db: AsyncSession = Depends(db_dependency), # noqa: B008
|
49
|
+
pagination: PaginationParams = Depends(), # noqa: B008
|
50
|
+
) -> PageResponse:
|
51
|
+
# Build the base query
|
52
|
+
query = select(model)
|
53
|
+
|
54
|
+
# Apply filtering if specified
|
55
|
+
if all(
|
56
|
+
[
|
57
|
+
pagination.filter_field,
|
58
|
+
pagination.filter_operator,
|
59
|
+
pagination.filter_value,
|
60
|
+
]
|
61
|
+
):
|
62
|
+
if field := getattr(model, pagination.filter_field, None):
|
63
|
+
query = _apply_filter(
|
64
|
+
query, field, pagination.filter_operator, pagination.filter_value
|
65
|
+
)
|
66
|
+
|
67
|
+
# Apply sorting if specified
|
68
|
+
if pagination.sort_by:
|
69
|
+
field = getattr(model, pagination.sort_by, None)
|
70
|
+
if field is not None:
|
71
|
+
query = query.order_by(
|
72
|
+
field.desc() if pagination.sort_order == "desc" else field
|
73
|
+
)
|
74
|
+
|
75
|
+
# Execute count query
|
76
|
+
count_query = select(func.count()).select_from(query.subquery())
|
77
|
+
total = await db.scalar(count_query) or 0
|
78
|
+
|
79
|
+
# Calculate pages
|
80
|
+
pages = (total + pagination.page_size - 1) // pagination.page_size
|
81
|
+
|
82
|
+
# Apply pagination
|
83
|
+
offset = (pagination.page - 1) * pagination.page_size
|
84
|
+
query = query.offset(offset).limit(pagination.page_size)
|
85
|
+
|
86
|
+
# Execute main query
|
87
|
+
result = await db.execute(query)
|
88
|
+
items = result.scalars().all()
|
89
|
+
|
90
|
+
# Calculate pagination flags
|
91
|
+
has_next = pagination.page < pages
|
92
|
+
has_previous = pagination.page > 1
|
93
|
+
|
94
|
+
# Create base pagination response
|
95
|
+
pagination_response = {
|
96
|
+
"items": items,
|
97
|
+
"total": total,
|
98
|
+
"page": pagination.page,
|
99
|
+
"pages": pages,
|
100
|
+
"page_size": pagination.page_size,
|
101
|
+
"has_next": has_next,
|
102
|
+
"has_previous": has_previous,
|
103
|
+
}
|
104
|
+
|
105
|
+
# Execute the original function to get any custom data
|
106
|
+
custom_data = await function(db=db, pagination=pagination)
|
107
|
+
print(custom_data)
|
108
|
+
if isinstance(custom_data, dict):
|
109
|
+
# Merge custom data with pagination data
|
110
|
+
pagination_response.update(custom_data)
|
111
|
+
print(pagination_response)
|
112
|
+
|
113
|
+
return pagination_response
|
114
|
+
|
115
|
+
return wrapper
|
116
|
+
|
117
|
+
return decorator
|
pagination/middleware.py
CHANGED
@@ -102,8 +102,6 @@ class PaginationMiddleware:
|
|
102
102
|
FilterOperator.LT: lambda f, v: f < v,
|
103
103
|
FilterOperator.GTE: lambda f, v: f >= v,
|
104
104
|
FilterOperator.LTE: lambda f, v: f <= v,
|
105
|
-
FilterOperator.LIKE: lambda f, v: f.like(f"%{v}%"),
|
106
|
-
FilterOperator.ILIKE: lambda f, v: f.ilike(f"%{v}%"),
|
107
105
|
FilterOperator.IN: lambda f, v: f.in_(v),
|
108
106
|
FilterOperator.NOT_IN: lambda f, v: ~f.in_(v),
|
109
107
|
}
|
pagination/models.py
CHANGED
@@ -39,8 +39,6 @@ class FilterOperator(str, Enum):
|
|
39
39
|
LT: Less than (<)
|
40
40
|
GTE: Greater than or equal (>=)
|
41
41
|
LTE: Less than or equal (<=)
|
42
|
-
LIKE: SQL LIKE pattern matching
|
43
|
-
ILIKE: Case-insensitive LIKE pattern matching
|
44
42
|
IN: Value in list
|
45
43
|
NOT_IN: Value not in list
|
46
44
|
"""
|
@@ -51,8 +49,6 @@ class FilterOperator(str, Enum):
|
|
51
49
|
LT = "lt" # less than
|
52
50
|
GTE = "gte" # greater than or equal
|
53
51
|
LTE = "lte" # less than or equal
|
54
|
-
LIKE = "like" # LIKE operator
|
55
|
-
ILIKE = "ilike" # ILIKE operator
|
56
52
|
IN = "in" # IN operator
|
57
53
|
NOT_IN = "not_in" # NOT IN operator
|
58
54
|
|
@@ -74,13 +70,13 @@ class PaginationParams(BaseModel):
|
|
74
70
|
filter_value: Value to filter by
|
75
71
|
"""
|
76
72
|
|
77
|
-
page: int = Field(default=1, gt=0)
|
78
|
-
page_size: int = Field(default=10, gt=0)
|
79
|
-
sort_by: str | None = None
|
80
|
-
sort_order: SortOrder = SortOrder.ASC
|
81
|
-
filter_field: str | None = None
|
82
|
-
filter_operator: FilterOperator | None = None
|
83
|
-
filter_value: Any | None = None
|
73
|
+
page: int = Field(default=1, gt=0, description="Page number")
|
74
|
+
page_size: int = Field(default=10, gt=0, le=100, description="Items per page")
|
75
|
+
sort_by: str | None = Field(default=None, description="Field to sort by")
|
76
|
+
sort_order: SortOrder = Field(default=SortOrder.ASC, description="Sort direction (asc/desc)")
|
77
|
+
filter_field: str | None = Field(default=None, description="Field to filter on")
|
78
|
+
filter_operator: FilterOperator | None = Field(default=None, description="Filter operator")
|
79
|
+
filter_value: Any | None = Field(default=None, description="Filter value")
|
84
80
|
|
85
81
|
model_config = ConfigDict(from_attributes=True)
|
86
82
|
|
@@ -1,7 +0,0 @@
|
|
1
|
-
pagination/__init__.py,sha256=Lr6iRikRx03cnFaV6n5e9s1yDAEOracpxbx6ImOA-v4,827
|
2
|
-
pagination/middleware.py,sha256=7PAzExzv55gBrZQORuJJORd1VTas8b-Oed-jm3eLquA,6261
|
3
|
-
pagination/models.py,sha256=nSASwuG-rlrti2RWI4Oj6epDiYGV0qCQmcijqGfMRbU,3248
|
4
|
-
paginate_fastapi-0.1.0.dist-info/LICENSE,sha256=04BsGfNMsEqA705-GhDsFirDIQyWUiCWJFx0h2ss6yE,1079
|
5
|
-
paginate_fastapi-0.1.0.dist-info/METADATA,sha256=TMdNZICsznUU2JXVHSeOeYNg_KTLAqP0TFXnVRR3QeU,5387
|
6
|
-
paginate_fastapi-0.1.0.dist-info/WHEEL,sha256=XbeZDeTWKc1w7CSIyre5aMDU_-PohRwTQceYnisIYYY,88
|
7
|
-
paginate_fastapi-0.1.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|