fastapi-sqla 3.0.2__py3-none-any.whl → 3.1.2__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.
- fastapi_sqla/__init__.py +8 -2
- fastapi_sqla/async_pagination.py +9 -9
- fastapi_sqla/async_sqla.py +1 -2
- fastapi_sqla/models.py +0 -12
- fastapi_sqla/pagination.py +16 -16
- fastapi_sqla/sqla.py +21 -2
- {fastapi_sqla-3.0.2.dist-info → fastapi_sqla-3.1.2.dist-info}/METADATA +42 -4
- fastapi_sqla-3.1.2.dist-info/RECORD +16 -0
- fastapi_sqla-3.0.2.dist-info/RECORD +0 -16
- {fastapi_sqla-3.0.2.dist-info → fastapi_sqla-3.1.2.dist-info}/LICENSE +0 -0
- {fastapi_sqla-3.0.2.dist-info → fastapi_sqla-3.1.2.dist-info}/WHEEL +0 -0
- {fastapi_sqla-3.0.2.dist-info → fastapi_sqla-3.1.2.dist-info}/entry_points.txt +0 -0
fastapi_sqla/__init__.py
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
from fastapi_sqla.base import setup
|
|
2
|
-
from fastapi_sqla.models import
|
|
2
|
+
from fastapi_sqla.models import Collection, Item, Page
|
|
3
3
|
from fastapi_sqla.pagination import Paginate, PaginateSignature, Pagination
|
|
4
|
-
from fastapi_sqla.sqla import
|
|
4
|
+
from fastapi_sqla.sqla import (
|
|
5
|
+
Base,
|
|
6
|
+
Session,
|
|
7
|
+
SessionDependency,
|
|
8
|
+
SqlaSession,
|
|
9
|
+
open_session,
|
|
10
|
+
)
|
|
5
11
|
|
|
6
12
|
__all__ = [
|
|
7
13
|
"Base",
|
fastapi_sqla/async_pagination.py
CHANGED
|
@@ -6,7 +6,7 @@ from fastapi import Depends, Query
|
|
|
6
6
|
from sqlalchemy.sql import Select, func, select
|
|
7
7
|
|
|
8
8
|
from fastapi_sqla.async_sqla import AsyncSessionDependency, SqlaAsyncSession
|
|
9
|
-
from fastapi_sqla.models import Page
|
|
9
|
+
from fastapi_sqla.models import Meta, Page
|
|
10
10
|
from fastapi_sqla.sqla import _DEFAULT_SESSION_KEY
|
|
11
11
|
|
|
12
12
|
QueryCountDependency = Callable[..., Awaitable[int]]
|
|
@@ -33,20 +33,20 @@ async def paginate_query(
|
|
|
33
33
|
scalars: bool = True,
|
|
34
34
|
) -> Page:
|
|
35
35
|
total_pages = math.ceil(total_items / limit)
|
|
36
|
-
page_number = offset / limit + 1
|
|
36
|
+
page_number = math.floor(offset / limit + 1)
|
|
37
37
|
query = query.offset(offset).limit(limit)
|
|
38
38
|
result = await session.execute(query)
|
|
39
39
|
data = iter(
|
|
40
40
|
cast(Iterator, result.unique().scalars() if scalars else result.mappings())
|
|
41
41
|
)
|
|
42
42
|
return Page(
|
|
43
|
-
data=data,
|
|
44
|
-
meta=
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
43
|
+
data=data, # type: ignore # Expected to be a list
|
|
44
|
+
meta=Meta(
|
|
45
|
+
offset=offset,
|
|
46
|
+
total_items=total_items,
|
|
47
|
+
total_pages=total_pages,
|
|
48
|
+
page_number=page_number,
|
|
49
|
+
),
|
|
50
50
|
)
|
|
51
51
|
|
|
52
52
|
|
fastapi_sqla/async_sqla.py
CHANGED
|
@@ -11,8 +11,7 @@ from sqlalchemy.ext.asyncio import AsyncSession as SqlaAsyncSession
|
|
|
11
11
|
from sqlalchemy.orm.session import sessionmaker
|
|
12
12
|
|
|
13
13
|
from fastapi_sqla import aws_aurora_support, aws_rds_iam_support
|
|
14
|
-
from fastapi_sqla.
|
|
15
|
-
from fastapi_sqla.sqla import _DEFAULT_SESSION_KEY, new_engine
|
|
14
|
+
from fastapi_sqla.sqla import _DEFAULT_SESSION_KEY, Base, new_engine
|
|
16
15
|
|
|
17
16
|
logger = structlog.get_logger(__name__)
|
|
18
17
|
|
fastapi_sqla/models.py
CHANGED
|
@@ -2,14 +2,6 @@ from typing import Generic, TypeVar
|
|
|
2
2
|
|
|
3
3
|
from pydantic import BaseModel, Field
|
|
4
4
|
from pydantic import __version__ as pydantic_version
|
|
5
|
-
from sqlalchemy.ext.declarative import DeferredReflection
|
|
6
|
-
|
|
7
|
-
try:
|
|
8
|
-
from sqlalchemy.orm import DeclarativeBase
|
|
9
|
-
except ImportError:
|
|
10
|
-
from sqlalchemy.ext.declarative import declarative_base
|
|
11
|
-
|
|
12
|
-
DeclarativeBase = declarative_base() # type: ignore
|
|
13
5
|
|
|
14
6
|
major, _, _ = [int(v) for v in pydantic_version.split(".")]
|
|
15
7
|
is_pydantic2 = major == 2
|
|
@@ -19,10 +11,6 @@ else:
|
|
|
19
11
|
from pydantic.generics import GenericModel # type:ignore
|
|
20
12
|
|
|
21
13
|
|
|
22
|
-
class Base(DeclarativeBase, DeferredReflection):
|
|
23
|
-
__abstract__ = True
|
|
24
|
-
|
|
25
|
-
|
|
26
14
|
ItemT = TypeVar("ItemT")
|
|
27
15
|
|
|
28
16
|
|
fastapi_sqla/pagination.py
CHANGED
|
@@ -7,7 +7,7 @@ from fastapi import Depends, Query
|
|
|
7
7
|
from sqlalchemy.orm import Query as LegacyQuery
|
|
8
8
|
from sqlalchemy.sql import Select, func, select
|
|
9
9
|
|
|
10
|
-
from fastapi_sqla.models import Page
|
|
10
|
+
from fastapi_sqla.models import Meta, Page
|
|
11
11
|
from fastapi_sqla.sqla import _DEFAULT_SESSION_KEY, SessionDependency, SqlaSession
|
|
12
12
|
|
|
13
13
|
DbQuery = Union[LegacyQuery, Select]
|
|
@@ -66,15 +66,15 @@ def _paginate_legacy(
|
|
|
66
66
|
scalars: bool = True,
|
|
67
67
|
) -> Page:
|
|
68
68
|
total_pages = math.ceil(total_items / limit)
|
|
69
|
-
page_number = offset / limit + 1
|
|
69
|
+
page_number = math.floor(offset / limit + 1)
|
|
70
70
|
return Page(
|
|
71
71
|
data=query.offset(offset).limit(limit).all(),
|
|
72
|
-
meta=
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
72
|
+
meta=Meta(
|
|
73
|
+
offset=offset,
|
|
74
|
+
total_items=total_items,
|
|
75
|
+
total_pages=total_pages,
|
|
76
|
+
page_number=page_number,
|
|
77
|
+
),
|
|
78
78
|
)
|
|
79
79
|
|
|
80
80
|
|
|
@@ -89,20 +89,20 @@ def _paginate(
|
|
|
89
89
|
scalars: bool = True,
|
|
90
90
|
) -> Page:
|
|
91
91
|
total_pages = math.ceil(total_items / limit)
|
|
92
|
-
page_number = offset / limit + 1
|
|
92
|
+
page_number = math.floor(offset / limit + 1)
|
|
93
93
|
query = query.offset(offset).limit(limit)
|
|
94
94
|
result = session.execute(query)
|
|
95
95
|
data = iter(
|
|
96
96
|
cast(Iterator, result.unique().scalars() if scalars else result.mappings())
|
|
97
97
|
)
|
|
98
98
|
return Page(
|
|
99
|
-
data=data,
|
|
100
|
-
meta=
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
99
|
+
data=data, # type: ignore # Expected to be a list
|
|
100
|
+
meta=Meta(
|
|
101
|
+
offset=offset,
|
|
102
|
+
total_items=total_items,
|
|
103
|
+
total_pages=total_pages,
|
|
104
|
+
page_number=page_number,
|
|
105
|
+
),
|
|
106
106
|
)
|
|
107
107
|
|
|
108
108
|
|
fastapi_sqla/sqla.py
CHANGED
|
@@ -10,11 +10,25 @@ from fastapi.concurrency import contextmanager_in_threadpool
|
|
|
10
10
|
from fastapi.responses import PlainTextResponse
|
|
11
11
|
from sqlalchemy import engine_from_config, text
|
|
12
12
|
from sqlalchemy.engine import Engine
|
|
13
|
+
from sqlalchemy.ext.declarative import DeferredReflection
|
|
13
14
|
from sqlalchemy.orm.session import Session as SqlaSession
|
|
14
15
|
from sqlalchemy.orm.session import sessionmaker
|
|
15
16
|
|
|
16
17
|
from fastapi_sqla import aws_aurora_support, aws_rds_iam_support
|
|
17
|
-
|
|
18
|
+
|
|
19
|
+
try:
|
|
20
|
+
from sqlalchemy.orm import DeclarativeBase
|
|
21
|
+
except ImportError:
|
|
22
|
+
from sqlalchemy.ext.declarative import declarative_base
|
|
23
|
+
|
|
24
|
+
DeclarativeBase = declarative_base() # type: ignore
|
|
25
|
+
|
|
26
|
+
try:
|
|
27
|
+
from sqlmodel import Session as SqlaSession # type: ignore # noqa
|
|
28
|
+
|
|
29
|
+
except ImportError:
|
|
30
|
+
pass
|
|
31
|
+
|
|
18
32
|
|
|
19
33
|
logger = structlog.get_logger(__name__)
|
|
20
34
|
|
|
@@ -23,6 +37,10 @@ _REQUEST_SESSION_KEY = "fastapi_sqla_session"
|
|
|
23
37
|
_session_factories: dict[str, sessionmaker] = {}
|
|
24
38
|
|
|
25
39
|
|
|
40
|
+
class Base(DeclarativeBase, DeferredReflection):
|
|
41
|
+
__abstract__ = True
|
|
42
|
+
|
|
43
|
+
|
|
26
44
|
def new_engine(key: str = _DEFAULT_SESSION_KEY) -> Engine:
|
|
27
45
|
envvar_prefix = "sqlalchemy_"
|
|
28
46
|
if key != _DEFAULT_SESSION_KEY:
|
|
@@ -50,7 +68,8 @@ def startup(key: str = _DEFAULT_SESSION_KEY):
|
|
|
50
68
|
raise
|
|
51
69
|
|
|
52
70
|
Base.prepare(engine)
|
|
53
|
-
|
|
71
|
+
|
|
72
|
+
_session_factories[key] = sessionmaker(bind=engine, class_=SqlaSession)
|
|
54
73
|
|
|
55
74
|
logger.info("engine startup", engine_key=key, engine=engine)
|
|
56
75
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: fastapi-sqla
|
|
3
|
-
Version: 3.
|
|
4
|
-
Summary: SQLAlchemy extension for FastAPI with support for pagination, asyncio, and pytest, ready for production.
|
|
3
|
+
Version: 3.1.2
|
|
4
|
+
Summary: SQLAlchemy extension for FastAPI with support for pagination, asyncio, SQLModel, and pytest, ready for production.
|
|
5
5
|
Home-page: https://github.com/dialoguemd/fastapi-sqla
|
|
6
6
|
License: MIT
|
|
7
7
|
Keywords: FastAPI,SQLAlchemy,asyncio,pytest,alembic
|
|
@@ -34,6 +34,7 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
|
34
34
|
Classifier: Typing :: Typed
|
|
35
35
|
Provides-Extra: asyncpg
|
|
36
36
|
Provides-Extra: aws-rds-iam
|
|
37
|
+
Provides-Extra: sqlmodel
|
|
37
38
|
Provides-Extra: tests
|
|
38
39
|
Requires-Dist: Faker (>=14.2.0,<15.0.0) ; extra == "tests"
|
|
39
40
|
Requires-Dist: alembic (>=1.4.3,<2.0.0) ; extra == "tests"
|
|
@@ -45,7 +46,7 @@ Requires-Dist: fastapi (>=0.95.1)
|
|
|
45
46
|
Requires-Dist: greenlet (>=1.1.3,<2.0.0) ; extra == "tests"
|
|
46
47
|
Requires-Dist: httpx (>=0.23.0,<0.24.0) ; extra == "tests"
|
|
47
48
|
Requires-Dist: isort (>=5.5.3,<6.0.0) ; extra == "tests"
|
|
48
|
-
Requires-Dist: mypy[tests] (>=0.
|
|
49
|
+
Requires-Dist: mypy[tests] (>=1.0.0,<2.0.0) ; extra == "tests"
|
|
49
50
|
Requires-Dist: pdbpp (>=0.10.2,<0.11.0) ; extra == "tests"
|
|
50
51
|
Requires-Dist: psycopg2 (>=2.8.6,<3.0.0) ; extra == "tests"
|
|
51
52
|
Requires-Dist: pydantic (>=1)
|
|
@@ -54,6 +55,7 @@ Requires-Dist: pytest (>=7.2.1,<8.0.0) ; extra == "tests"
|
|
|
54
55
|
Requires-Dist: pytest-asyncio (>=0.19.0,<0.20.0) ; extra == "tests"
|
|
55
56
|
Requires-Dist: pytest-cov (>=2.10.1,<3.0.0) ; extra == "tests"
|
|
56
57
|
Requires-Dist: sqlalchemy (>=1.3)
|
|
58
|
+
Requires-Dist: sqlmodel (>=0.0.14,<0.0.15) ; extra == "sqlmodel"
|
|
57
59
|
Requires-Dist: structlog (>=20)
|
|
58
60
|
Requires-Dist: tox (>=3.26.0,<4.0.0) ; extra == "tests"
|
|
59
61
|
Project-URL: Repository, https://github.com/dialoguemd/fastapi-sqla
|
|
@@ -69,7 +71,7 @@ Description-Content-Type: text/markdown
|
|
|
69
71
|
|
|
70
72
|
|
|
71
73
|
Fastapi-SQLA is an [SQLAlchemy] extension for [FastAPI] easy to setup with support for
|
|
72
|
-
pagination, asyncio, and [pytest].
|
|
74
|
+
pagination, asyncio, [SQLModel] and [pytest].
|
|
73
75
|
It supports SQLAlchemy>=1.3 and is fully compliant with [SQLAlchemy 2.0].
|
|
74
76
|
It is developped, maintained and used on production by the team at [@dialoguemd] with
|
|
75
77
|
love from Montreal 🇨🇦.
|
|
@@ -600,6 +602,41 @@ async def async_all_users_alt(
|
|
|
600
602
|
return await paginate(select(User))
|
|
601
603
|
```
|
|
602
604
|
|
|
605
|
+
# SQLModel support 🎉
|
|
606
|
+
|
|
607
|
+
If your project uses [SQLModel], then `Session` dependency is an SQLModel session::
|
|
608
|
+
|
|
609
|
+
```python
|
|
610
|
+
from http import HTTPStatus
|
|
611
|
+
|
|
612
|
+
from fastapi import FastAPI, HTTPException
|
|
613
|
+
from fastapi_sqla import Item, Page, Paginate, Session, setup
|
|
614
|
+
from sqlmodel import Field, SQLModel, select
|
|
615
|
+
|
|
616
|
+
class Hero(SQLModel, table=True):
|
|
617
|
+
id: int | None = Field(default=None, primary_key=True)
|
|
618
|
+
name: str
|
|
619
|
+
secret_name: str
|
|
620
|
+
age: int | None = None
|
|
621
|
+
|
|
622
|
+
|
|
623
|
+
app = FastAPI()
|
|
624
|
+
setup(app)
|
|
625
|
+
|
|
626
|
+
@app.get("/heros", response_model=Page[Hero])
|
|
627
|
+
def list_hero(paginate: Paginate) -> Page[Hero]:
|
|
628
|
+
return paginate(select(Hero))
|
|
629
|
+
|
|
630
|
+
|
|
631
|
+
@app.get("/heros/{hero_id}", response_model=Item[Hero])
|
|
632
|
+
def get_hero(hero_id: int, session: Session) -> Item[Hero]:
|
|
633
|
+
hero = session.get(Hero, hero_id)
|
|
634
|
+
if hero is None:
|
|
635
|
+
raise HTTPException(HTTPStatus.NOT_FOUND)
|
|
636
|
+
return {"data": hero}
|
|
637
|
+
|
|
638
|
+
```
|
|
639
|
+
|
|
603
640
|
# Pytest fixtures
|
|
604
641
|
|
|
605
642
|
This library provides a set of utility fixtures, through its PyTest plugin, which is
|
|
@@ -770,6 +807,7 @@ $ poetry run tox
|
|
|
770
807
|
[FastAPI background tasks]: https://fastapi.tiangolo.com/tutorial/background-tasks/
|
|
771
808
|
[SQLAlchemy]: http://sqlalchemy.org/
|
|
772
809
|
[SQLAlchemy 2.0]: https://docs.sqlalchemy.org/en/20/changelog/migration_20.html
|
|
810
|
+
[SQLModel]: https://sqlmodel.tiangolo.com
|
|
773
811
|
[`asyncpg`]: https://magicstack.github.io/asyncpg/current/
|
|
774
812
|
[scalars]: https://docs.sqlalchemy.org/en/20/core/connections.html#sqlalchemy.engine.Result.scalars
|
|
775
813
|
[alembic]: https://alembic.sqlalchemy.org/
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
fastapi_sqla/__init__.py,sha256=G9bKR6L9EnLgzelE_BLnHgc1cOm0Yu0wMzwLAdyA8iE,1153
|
|
2
|
+
fastapi_sqla/_pytest_plugin.py,sha256=bjPzOuwk32Zo4hoOa_bGjTGhtHGPImh3RyiORqz-J84,5430
|
|
3
|
+
fastapi_sqla/async_pagination.py,sha256=jYeZYtIi7mssM3MfwobOoaPYNZF0BE1EmCK8CqFnwOg,3164
|
|
4
|
+
fastapi_sqla/async_sqla.py,sha256=7SXRH3DMsQvpjCQdvCBEjQhDZ4-cPAg175MlgNVBnCg,5888
|
|
5
|
+
fastapi_sqla/aws_aurora_support.py,sha256=4dxLKOqDccgLwFqlz81L6f4HzrOXMZkY7Zuf4t_310U,838
|
|
6
|
+
fastapi_sqla/aws_rds_iam_support.py,sha256=YSJNhrxmhGN-GVk9PLMTmQSWTKZBvuorKkhc_XaoL44,1189
|
|
7
|
+
fastapi_sqla/base.py,sha256=0X7Gbt49rBHPiSFmNy5S2PT0dA4UBNnwrAesYSkaHBc,1606
|
|
8
|
+
fastapi_sqla/models.py,sha256=fesW7BqkwOA4iC345dbybzcV8Kz4-kLwREo38oqy_7A,1108
|
|
9
|
+
fastapi_sqla/pagination.py,sha256=IjbKFyUHly8jHyOO4pnyPK4RE_cFRDY03OOZeZEm3cY,4513
|
|
10
|
+
fastapi_sqla/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
11
|
+
fastapi_sqla/sqla.py,sha256=lOikCu_BjGQYfbbtcM59ZjwTL-66cM9L_yIbCEQasAQ,6356
|
|
12
|
+
fastapi_sqla-3.1.2.dist-info/LICENSE,sha256=8G0-nWLqi3xRYRrtRlTE8n1mkYJcnCRoZGUhv6ZE29c,1064
|
|
13
|
+
fastapi_sqla-3.1.2.dist-info/METADATA,sha256=xl5jfdozvDDEJGHW0fe9bFm_ITasdlzYb3NThMDveNk,20915
|
|
14
|
+
fastapi_sqla-3.1.2.dist-info/WHEEL,sha256=Zb28QaM1gQi8f4VCBhsUklF61CTlNYfs9YAZn-TOGFk,88
|
|
15
|
+
fastapi_sqla-3.1.2.dist-info/entry_points.txt,sha256=haa0EueKcRo8-AlJTpHBMn08wMBiULNGA53nkvaDWj0,53
|
|
16
|
+
fastapi_sqla-3.1.2.dist-info/RECORD,,
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
fastapi_sqla/__init__.py,sha256=4-MHiYF6TclnQpgs2Z1KDwYe2OPHLHGEu5oJzV3494s,1128
|
|
2
|
-
fastapi_sqla/_pytest_plugin.py,sha256=bjPzOuwk32Zo4hoOa_bGjTGhtHGPImh3RyiORqz-J84,5430
|
|
3
|
-
fastapi_sqla/async_pagination.py,sha256=UA3KxkTa48utBFDHeAkOFxTGUIvtNEWKm4uH-bqzQH4,3114
|
|
4
|
-
fastapi_sqla/async_sqla.py,sha256=BERggjeIjBW9hPqVxE7ry3iQyjBFc9KLvDGwV_LOfK8,5919
|
|
5
|
-
fastapi_sqla/aws_aurora_support.py,sha256=4dxLKOqDccgLwFqlz81L6f4HzrOXMZkY7Zuf4t_310U,838
|
|
6
|
-
fastapi_sqla/aws_rds_iam_support.py,sha256=YSJNhrxmhGN-GVk9PLMTmQSWTKZBvuorKkhc_XaoL44,1189
|
|
7
|
-
fastapi_sqla/base.py,sha256=0X7Gbt49rBHPiSFmNy5S2PT0dA4UBNnwrAesYSkaHBc,1606
|
|
8
|
-
fastapi_sqla/models.py,sha256=QhnPCX-Gz5exAZfWyCRyYSaZ7SM_8QY0Eir6b4_4oI8,1432
|
|
9
|
-
fastapi_sqla/pagination.py,sha256=NsI4ZeOkgbiNNDBjtqZL1rF6j1ya--jjXiyf0GlLaXU,4459
|
|
10
|
-
fastapi_sqla/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
11
|
-
fastapi_sqla/sqla.py,sha256=ih4JBAQzn-lT86jK5ZOX3tewT1UOrw2NUJauOso0mNg,5939
|
|
12
|
-
fastapi_sqla-3.0.2.dist-info/LICENSE,sha256=8G0-nWLqi3xRYRrtRlTE8n1mkYJcnCRoZGUhv6ZE29c,1064
|
|
13
|
-
fastapi_sqla-3.0.2.dist-info/METADATA,sha256=hS61rcvqSFKrKWkfN6y5xpLOTRhnbvi-j0OTa5gVVAY,19809
|
|
14
|
-
fastapi_sqla-3.0.2.dist-info/WHEEL,sha256=Zb28QaM1gQi8f4VCBhsUklF61CTlNYfs9YAZn-TOGFk,88
|
|
15
|
-
fastapi_sqla-3.0.2.dist-info/entry_points.txt,sha256=haa0EueKcRo8-AlJTpHBMn08wMBiULNGA53nkvaDWj0,53
|
|
16
|
-
fastapi_sqla-3.0.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|