paginate-fastapi 0.1.0__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2024 Ritvik Dayal
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,212 @@
1
+ Metadata-Version: 2.3
2
+ Name: paginate-fastapi
3
+ Version: 0.1.0
4
+ Summary: A simple and efficient pagination library for FastAPI applications
5
+ License: MIT
6
+ Keywords: fastapi,sqlmodel,pagination,async,filtering,sorting
7
+ Author: Ritvik Dayal
8
+ Author-email: ritvikr1605@gmail.com
9
+ Requires-Python: >=3.11
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Framework :: FastAPI
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
18
+ Classifier: Typing :: Typed
19
+ Provides-Extra: dev
20
+ Requires-Dist: aiosqlite (>=0.20.0) ; extra == "dev"
21
+ Requires-Dist: black (>=24.1.0) ; extra == "dev"
22
+ Requires-Dist: fastapi (>=0.100.0)
23
+ Requires-Dist: fastapi[all] (>=0.100.0) ; extra == "dev"
24
+ Requires-Dist: httpx (>=0.27.0) ; extra == "dev"
25
+ Requires-Dist: mypy (>=1.8.0) ; extra == "dev"
26
+ Requires-Dist: pre-commit (>=4.1.0,<5.0.0)
27
+ Requires-Dist: pydantic (>=2.0.0)
28
+ Requires-Dist: pytest (>=8.0.0) ; extra == "dev"
29
+ Requires-Dist: pytest-asyncio (>=0.23.0) ; extra == "dev"
30
+ Requires-Dist: pytest-cov (>=4.1.0) ; extra == "dev"
31
+ Requires-Dist: ruff (>=0.9.7,<0.10.0) ; extra == "dev"
32
+ Requires-Dist: sqlmodel (>=0.0.8)
33
+ Project-URL: Documentation, https://github.com/ritvikdayal/paginate-fastapi#readme
34
+ Project-URL: Homepage, https://github.com/ritvikdayal/paginate-fastapi
35
+ Project-URL: Issues, https://github.com/ritvikdayal/paginate-fastapi/issues
36
+ Project-URL: Repository, https://github.com/ritvikdayal/paginate-fastapi.git
37
+ Description-Content-Type: text/markdown
38
+
39
+ # Paginate FastAPI
40
+
41
+ [![PyPI version](https://badge.fury.io/py/paginate-fastapi.svg)](https://badge.fury.io/py/paginate-fastapi)
42
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
43
+ [![Python 3.11+](https://img.shields.io/badge/python-3.11+-blue.svg)](https://www.python.org/downloads/)
44
+
45
+ A simple and efficient pagination library for FastAPI applications.
46
+
47
+ ## Features
48
+
49
+ - Easy-to-use pagination with FastAPI
50
+ - Async support out of the box
51
+ - Flexible filtering options
52
+ - Customizable sorting
53
+ - Type-safe with full type hints
54
+ - Compatible with FastAPI
55
+
56
+ ## Installation
57
+
58
+ ### Using Poetry
59
+ ```bash
60
+ poetry add paginate-fastapi
61
+ ```
62
+
63
+ ### Using Pip
64
+ ```bash
65
+ pip install paginate-fastapi
66
+ ```
67
+
68
+ ## Quick Start
69
+
70
+ ```python
71
+ from fastapi import FastAPI, Depends
72
+ from sqlmodel import SQLModel, Field
73
+ from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
74
+ from pagination import PaginationMiddleware, PaginationParams
75
+
76
+ app = FastAPI()
77
+
78
+ # Initialize your database
79
+ engine = create_async_engine("sqlite+aiosqlite:///database.db")
80
+
81
+ async def get_session() -> AsyncSession:
82
+ async with AsyncSession(engine) as session:
83
+ yield session
84
+
85
+ paginator = PaginationMiddleware(get_session)
86
+
87
+ # Define your model
88
+ class User(SQLModel, table=True):
89
+ id: int = Field(primary_key=True)
90
+ name: str
91
+ email: str
92
+ age: int
93
+
94
+ # Add pagination to your endpoint
95
+ @app.get("/users/")
96
+ async def get_users(
97
+ pagination: PaginationParams = Depends(),
98
+ paginator: PaginationMiddleware = Depends(lambda: paginator),
99
+ ):
100
+ return await paginator.paginate(User, pagination)
101
+ ```
102
+
103
+ ### Sample Request and Response
104
+
105
+ ```bash
106
+ curl -X GET "http://localhost:8000/users/?page=1&page_size=10"
107
+ ```
108
+
109
+ ```json
110
+ {
111
+ "items": [
112
+ {
113
+ "id": 1,
114
+ "name": "John Doe",
115
+ "email": "john.doe@example.com",
116
+ "age": 30
117
+ }
118
+ ],
119
+ "total": 100,
120
+ "page": 1,
121
+ "page_size": 10,
122
+ "pages": 10,
123
+ "has_next": true,
124
+ "has_previous": false
125
+ }
126
+ ```
127
+
128
+ ### Sorting
129
+
130
+ ```bash
131
+ # Sort by name ascending
132
+ users/?sort_by=name&sort_order=asc
133
+
134
+ # Sort by age descending
135
+ users/?sort_by=age&sort_order=desc
136
+ ```
137
+
138
+ ### Filtering
139
+
140
+ ```bash
141
+ # Filter users by age greater than 30
142
+ users/?filter_field=age&filter_operator=gt&filter_value=30
143
+
144
+ # Filter users by name containing "John"
145
+ users/?filter_field=name&filter_operator=like&filter_value=John
146
+
147
+ # Filter users by age in a list
148
+ users/?filter_field=age&filter_operator=in&filter_value=[25,30,35]
149
+ ```
150
+
151
+ ### Available Filter Operators
152
+
153
+ - `eq`: Equal to
154
+ - `ne`: Not equal to
155
+ - `gt`: Greater than
156
+ - `lt`: Less than
157
+ - `ge`: Greater than or equal to
158
+ - `le`: Less than or equal to
159
+ - `like`: Contains (case-sensitive)
160
+ - `ilike`: Contains (case-insensitive)
161
+ - `in`: In list of values
162
+ - `not_in`: Not in list of values
163
+
164
+ ## Development
165
+
166
+ ### Setup
167
+
168
+ ```bash
169
+ # Clone the repository
170
+ git clone https://github.com/ritvikdayal/paginate-fastapi.git
171
+ cd paginate-fastapi
172
+
173
+ # Install dependencies
174
+ poetry install --with dev
175
+
176
+ # Setup pre-commit hooks (optional)
177
+ make setup-hooks
178
+ ```
179
+
180
+ ### Running Tests
181
+
182
+ ```bash
183
+ make test
184
+ ```
185
+
186
+ ### Code Quality
187
+
188
+ ```bash
189
+ # Run all code quality checks
190
+ make pre-commit
191
+
192
+ # Format code only
193
+ make format
194
+
195
+ # Run linters only
196
+ make lint
197
+ ```
198
+
199
+ ## Contributing
200
+
201
+ Contributions are welcome! Please feel free to submit a Pull Request.
202
+
203
+ 1. Fork the repository
204
+ 2. Create your feature branch (`git checkout -b feature/amazing-feature`)
205
+ 3. Commit your changes (`git commit -m 'Add some amazing feature'`)
206
+ 4. Push to the branch (`git push origin feature/amazing-feature`)
207
+ 5. Open a Pull Request
208
+
209
+ ## License
210
+
211
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
212
+
@@ -0,0 +1,7 @@
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,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: poetry-core 2.1.1
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
pagination/__init__.py ADDED
@@ -0,0 +1,31 @@
1
+ """
2
+ FastAPI SQLModel Pagination Library
3
+
4
+ A library for adding pagination, filtering, and sorting capabilities
5
+ to FastAPI applications using SQLModel.
6
+
7
+ Example:
8
+ from fastapi import FastAPI, Depends
9
+ from pagination import PaginationMiddleware, PaginationParams
10
+
11
+ app = FastAPI()
12
+ paginator = PaginationMiddleware(get_session)
13
+
14
+ @app.get("/items/")
15
+ async def get_items(
16
+ pagination: PaginationParams = Depends(),
17
+ paginator: PaginationMiddleware = Depends(lambda: paginator),
18
+ ):
19
+ return await paginator.paginate(Item, pagination)
20
+ """
21
+
22
+ from .middleware import PaginationMiddleware
23
+ from .models import FilterOperator, PageResponse, PaginationParams, SortOrder
24
+
25
+ __all__ = [
26
+ "FilterOperator",
27
+ "PageResponse",
28
+ "PaginationMiddleware",
29
+ "PaginationParams",
30
+ "SortOrder",
31
+ ]
@@ -0,0 +1,182 @@
1
+ """
2
+ Pagination middleware for FastAPI applications.
3
+
4
+ This module provides the core pagination functionality, including:
5
+ - Async session handling
6
+ - Query building with filters
7
+ - Sorting implementation
8
+ - Pagination calculation
9
+
10
+ The middleware can be used with any SQLModel-based FastAPI application
11
+ to add pagination, filtering, and sorting capabilities.
12
+ """
13
+
14
+ from collections.abc import AsyncGenerator, Callable
15
+ from contextlib import asynccontextmanager
16
+
17
+ from sqlalchemy import asc, desc
18
+ from sqlalchemy.ext.asyncio import AsyncSession
19
+ from sqlalchemy.sql import Select
20
+ from sqlmodel import SQLModel, func, select
21
+
22
+ from .models import FilterOperator, PageResponse, PaginationParams, SortOrder
23
+
24
+
25
+ class PaginationMiddleware:
26
+ """
27
+ Middleware for handling pagination in FastAPI applications.
28
+
29
+ This class provides methods to paginate SQLModel queries with
30
+ support for filtering and sorting. It handles both async context
31
+ managers and async generators for database sessions.
32
+
33
+ Attributes:
34
+ session_maker: Callable that provides database sessions
35
+ default_page_size: Default number of items per page
36
+ """
37
+
38
+ def __init__(
39
+ self,
40
+ session_maker: Callable[[], AsyncSession | AsyncGenerator[AsyncSession, None]],
41
+ default_page_size: int = 10,
42
+ ):
43
+ """
44
+ Initialize the pagination middleware.
45
+
46
+ Args:
47
+ session_maker: Function that returns a database session
48
+ default_page_size: Default number of items per page
49
+ """
50
+ self.session_maker = session_maker
51
+ self.default_page_size = default_page_size
52
+
53
+ @asynccontextmanager
54
+ async def get_session(self) -> AsyncGenerator[AsyncSession, None]:
55
+ """
56
+ Get a database session using the session maker.
57
+
58
+ This method handles both async context managers and async generators,
59
+ making it compatible with FastAPI dependency injection.
60
+
61
+ Yields:
62
+ AsyncSession: Database session
63
+ """
64
+ session_factory = self.session_maker()
65
+ if hasattr(session_factory, "__aenter__"):
66
+ async with session_factory as session:
67
+ yield session
68
+ else:
69
+ try:
70
+ session = await anext(session_factory)
71
+ yield session
72
+ finally:
73
+ try:
74
+ await session_factory.aclose()
75
+ except RuntimeError as ex:
76
+ if "already closed" not in str(ex):
77
+ raise ex
78
+
79
+ def _apply_filter(
80
+ self, query: Select, model: type[SQLModel], params: PaginationParams
81
+ ) -> Select:
82
+ """
83
+ Apply filter conditions to the query.
84
+
85
+ Args:
86
+ query: Base SQLAlchemy select query
87
+ model: SQLModel class to query
88
+ params: Pagination parameters containing filter settings
89
+
90
+ Returns:
91
+ Select: Query with filters applied
92
+ """
93
+ if not all([params.filter_field, params.filter_operator, params.filter_value is not None]):
94
+ return query
95
+
96
+ field = getattr(model, params.filter_field)
97
+
98
+ filter_map = {
99
+ FilterOperator.EQ: lambda f, v: f == v,
100
+ FilterOperator.NEQ: lambda f, v: f != v,
101
+ FilterOperator.GT: lambda f, v: f > v,
102
+ FilterOperator.LT: lambda f, v: f < v,
103
+ FilterOperator.GTE: lambda f, v: f >= v,
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
+ FilterOperator.IN: lambda f, v: f.in_(v),
108
+ FilterOperator.NOT_IN: lambda f, v: ~f.in_(v),
109
+ }
110
+
111
+ filter_func = filter_map[params.filter_operator]
112
+ return query.where(filter_func(field, params.filter_value))
113
+
114
+ def _apply_sort(self, query: Select, model: type[SQLModel], params: PaginationParams) -> Select:
115
+ """
116
+ Apply sorting to the query.
117
+
118
+ Args:
119
+ query: Base SQLAlchemy select query
120
+ model: SQLModel class to query
121
+ params: Pagination parameters containing sort settings
122
+
123
+ Returns:
124
+ Select: Query with sorting applied
125
+ """
126
+ if not params.sort_by:
127
+ return query
128
+
129
+ field = getattr(model, params.sort_by)
130
+ return query.order_by(desc(field) if params.sort_order == SortOrder.DESC else asc(field))
131
+
132
+ async def paginate(
133
+ self, model: type[SQLModel], params: PaginationParams | None = None
134
+ ) -> PageResponse:
135
+ """
136
+ Paginate a SQLModel query with optional filtering and sorting.
137
+
138
+ This method handles the complete pagination process:
139
+ 1. Builds the base query
140
+ 2. Applies any filters
141
+ 3. Applies sorting
142
+ 4. Calculates total count
143
+ 5. Applies pagination
144
+ 6. Returns formatted response
145
+
146
+ Args:
147
+ model: SQLModel class to paginate
148
+ params: Pagination, filtering, and sorting parameters
149
+
150
+ Returns:
151
+ PageResponse: Paginated results with metadata
152
+ """
153
+ if params is None:
154
+ params = PaginationParams(page_size=self.default_page_size)
155
+
156
+ async with self.get_session() as session:
157
+ # Build query
158
+ query = select(model)
159
+ query = self._apply_filter(query, model, params)
160
+ query = self._apply_sort(query, model, params)
161
+
162
+ # Get total count
163
+ count_query = select(func.count()).select_from(query.subquery())
164
+ total = (await session.execute(count_query)).scalar() or 0
165
+
166
+ # Apply pagination
167
+ query = query.offset(params.offset).limit(params.page_size)
168
+ result = await session.execute(query)
169
+ items = result.scalars().all()
170
+
171
+ # Calculate pagination metadata
172
+ pages = (total + params.page_size - 1) // params.page_size if total > 0 else 0
173
+
174
+ return PageResponse(
175
+ items=items,
176
+ total=total,
177
+ page=params.page,
178
+ page_size=params.page_size,
179
+ pages=pages,
180
+ has_next=params.page < pages,
181
+ has_previous=params.page > 1,
182
+ )
pagination/models.py ADDED
@@ -0,0 +1,122 @@
1
+ """
2
+ Models for the pagination library.
3
+
4
+ This module contains the core data models used for pagination, filtering, and sorting.
5
+ It defines the parameters that can be used to control pagination behavior and the
6
+ structure of pagination responses.
7
+ """
8
+
9
+ from collections.abc import Sequence
10
+ from enum import Enum
11
+ from typing import Any, Generic, TypeVar
12
+
13
+ from pydantic import BaseModel, ConfigDict, Field
14
+
15
+ T = TypeVar("T")
16
+
17
+
18
+ class SortOrder(str, Enum):
19
+ """
20
+ Enumeration for sort order direction.
21
+
22
+ Attributes:
23
+ ASC: Ascending order (A to Z, 1 to 9)
24
+ DESC: Descending order (Z to A, 9 to 1)
25
+ """
26
+
27
+ ASC = "asc"
28
+ DESC = "desc"
29
+
30
+
31
+ class FilterOperator(str, Enum):
32
+ """
33
+ Enumeration for filter operations.
34
+
35
+ Attributes:
36
+ EQ: Equals (=)
37
+ NEQ: Not equals (!=)
38
+ GT: Greater than (>)
39
+ LT: Less than (<)
40
+ GTE: Greater than or equal (>=)
41
+ LTE: Less than or equal (<=)
42
+ LIKE: SQL LIKE pattern matching
43
+ ILIKE: Case-insensitive LIKE pattern matching
44
+ IN: Value in list
45
+ NOT_IN: Value not in list
46
+ """
47
+
48
+ EQ = "eq" # equals
49
+ NEQ = "neq" # not equals
50
+ GT = "gt" # greater than
51
+ LT = "lt" # less than
52
+ GTE = "gte" # greater than or equal
53
+ LTE = "lte" # less than or equal
54
+ LIKE = "like" # LIKE operator
55
+ ILIKE = "ilike" # ILIKE operator
56
+ IN = "in" # IN operator
57
+ NOT_IN = "not_in" # NOT IN operator
58
+
59
+
60
+ class PaginationParams(BaseModel):
61
+ """
62
+ Parameters for pagination, filtering, and sorting.
63
+
64
+ This model can be used directly as a FastAPI dependency to receive
65
+ pagination parameters from query strings.
66
+
67
+ Attributes:
68
+ page: Current page number (1-based)
69
+ page_size: Number of items per page
70
+ sort_by: Field name to sort by
71
+ sort_order: Sort direction (asc/desc)
72
+ filter_field: Field name to filter on
73
+ filter_operator: Filter operation to apply
74
+ filter_value: Value to filter by
75
+ """
76
+
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
84
+
85
+ model_config = ConfigDict(from_attributes=True)
86
+
87
+ @property
88
+ def offset(self) -> int:
89
+ """
90
+ Calculate the SQL offset for the current page.
91
+
92
+ Returns:
93
+ int: Number of items to skip
94
+ """
95
+ return (self.page - 1) * self.page_size
96
+
97
+
98
+ class PageResponse(BaseModel, Generic[T]):
99
+ """
100
+ Generic response model for paginated results.
101
+
102
+ Type parameter T represents the model type being paginated.
103
+
104
+ Attributes:
105
+ items: Sequence of items for the current page
106
+ total: Total number of items across all pages
107
+ page: Current page number
108
+ page_size: Number of items per page
109
+ pages: Total number of pages
110
+ has_next: Whether there is a next page
111
+ has_previous: Whether there is a previous page
112
+ """
113
+
114
+ items: Sequence[T]
115
+ total: int
116
+ page: int
117
+ page_size: int
118
+ pages: int
119
+ has_next: bool
120
+ has_previous: bool
121
+
122
+ model_config = ConfigDict(from_attributes=True)