paginate-fastapi 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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)