Skip to content

Commit 3df6b5e

Browse files
NatalyaGrigorevaNatalia Grigoreva
authored andcommitted
update sqlalchemy package to v2
1 parent 28088c3 commit 3df6b5e

File tree

116 files changed

+1133
-2009
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

116 files changed

+1133
-2009
lines changed

README.md

Lines changed: 38 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -31,34 +31,34 @@ Create a test.py file and copy the following code into it
3131

3232
```python
3333
from pathlib import Path
34-
from typing import Any, ClassVar
34+
from typing import Any, ClassVar, Optional
3535

3636
import uvicorn
3737
from fastapi import APIRouter, Depends, FastAPI
3838
from pydantic import ConfigDict
39-
from sqlalchemy import Column, Integer, Text
4039
from sqlalchemy.engine import make_url
41-
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
42-
from sqlalchemy.ext.declarative import declarative_base
43-
from sqlalchemy.orm import sessionmaker
40+
from sqlalchemy.ext.asyncio import AsyncEngine, AsyncSession, async_sessionmaker, create_async_engine
41+
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
4442

4543
from fastapi_jsonapi import RoutersJSONAPI, init
4644
from fastapi_jsonapi.misc.sqla.generics.base import DetailViewBaseGeneric, ListViewBaseGeneric
4745
from fastapi_jsonapi.schema_base import BaseModel
4846
from fastapi_jsonapi.views.utils import HTTPMethod, HTTPMethodConfig
4947
from fastapi_jsonapi.views.view_base import ViewBase
5048

51-
CURRENT_FILE = Path(__file__).resolve()
52-
CURRENT_DIR = CURRENT_FILE.parent
49+
CURRENT_DIR = Path(__file__).resolve().parent
5350
DB_URL = f"sqlite+aiosqlite:///{CURRENT_DIR}/db.sqlite3"
5451

55-
Base = declarative_base()
52+
53+
class Base(DeclarativeBase):
54+
pass
5655

5756

5857
class User(Base):
5958
__tablename__ = "users"
60-
id = Column(Integer, primary_key=True, autoincrement=True)
61-
name = Column(Text, nullable=True)
59+
60+
id: Mapped[int] = mapped_column(primary_key=True)
61+
name: Mapped[Optional[str]]
6262

6363

6464
class UserAttributesBaseSchema(BaseModel):
@@ -81,38 +81,44 @@ class UserInSchema(UserAttributesBaseSchema):
8181
"""User input schema."""
8282

8383

84-
def async_session() -> sessionmaker:
85-
engine = create_async_engine(url=make_url(DB_URL))
86-
_async_session = sessionmaker(bind=engine, class_=AsyncSession, expire_on_commit=False)
87-
return _async_session
84+
def async_session() -> tuple[AsyncEngine, async_sessionmaker]:
85+
engine_: AsyncEngine = create_async_engine(
86+
url=f"{make_url(DB_URL)}",
87+
echo=True,
88+
)
89+
session_maker_: async_sessionmaker[AsyncSession] = async_sessionmaker(
90+
autocommit=False,
91+
bind=engine,
92+
expire_on_commit=False,
93+
)
94+
return engine_, session_maker_
95+
96+
97+
engine, session_maker = async_session()
8898

8999

90100
class Connector:
91101
@classmethod
92-
async def get_session(cls):
93-
"""
94-
Get session as dependency
95-
96-
:return:
97-
"""
98-
sess = async_session()
99-
async with sess() as db_session: # type: AsyncSession
100-
yield db_session
101-
await db_session.rollback()
102+
async def dispose(cls):
103+
await engine.dispose()
102104

105+
@classmethod
106+
async def init(cls) -> None:
107+
async with engine.begin() as conn:
108+
await conn.run_sync(Base.metadata.create_all)
103109

104-
async def sqlalchemy_init() -> None:
105-
engine = create_async_engine(url=make_url(DB_URL))
106-
async with engine.begin() as conn:
107-
await conn.run_sync(Base.metadata.create_all)
110+
@classmethod
111+
async def session(cls):
112+
async with session_maker() as db_session:
113+
yield db_session
108114

109115

110116
class SessionDependency(BaseModel):
111117
model_config = ConfigDict(
112118
arbitrary_types_allowed=True,
113119
)
114120

115-
session: AsyncSession = Depends(Connector.get_session)
121+
session: AsyncSession = Depends(Connector.session)
116122

117123

118124
def session_dependency_handler(view: ViewBase, dto: SessionDependency) -> dict[str, Any]:
@@ -178,13 +184,15 @@ def create_app() -> FastAPI:
178184
docs_url="/docs",
179185
)
180186
add_routes(app)
181-
app.on_event("startup")(sqlalchemy_init)
187+
app.on_event("startup")(Connector.init)
188+
app.on_event("shutdown")(Connector.dispose)
182189
init(app)
183190
return app
184191

185192

186193
app = create_app()
187194

195+
188196
if __name__ == "__main__":
189197
uvicorn.run(
190198
"main:app",

docs/python_snippets/client_generated_id/schematic_example.py

Lines changed: 19 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,39 @@
11
import sys
22
from pathlib import Path
3-
from typing import ClassVar, Annotated
3+
from typing import ClassVar, Annotated, Optional
44

55
import uvicorn
66
from fastapi import APIRouter, Depends, FastAPI
7-
from fastapi_jsonapi.schema_base import BaseModel
87
from pydantic import ConfigDict
9-
from sqlalchemy import Column, Integer, Text
108
from sqlalchemy.engine import make_url
11-
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
12-
from sqlalchemy.ext.declarative import declarative_base
13-
from sqlalchemy.orm import sessionmaker
9+
from sqlalchemy.ext.asyncio import AsyncSession
10+
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
1411

12+
from examples.api_for_sqlalchemy.models.db import DB
1513
from fastapi_jsonapi import RoutersJSONAPI, init
1614
from fastapi_jsonapi.misc.sqla.generics.base import DetailViewBaseGeneric, ListViewBaseGeneric
15+
from fastapi_jsonapi.schema_base import BaseModel
1716
from fastapi_jsonapi.types_metadata import ClientCanSetId
1817
from fastapi_jsonapi.views.utils import HTTPMethod, HTTPMethodConfig
1918
from fastapi_jsonapi.views.view_base import ViewBase
2019

2120
CURRENT_FILE = Path(__file__).resolve()
2221
CURRENT_DIR = CURRENT_FILE.parent
23-
PROJECT_DIR = CURRENT_DIR.parent.parent
24-
DB_URL = f"sqlite+aiosqlite:///{CURRENT_DIR.absolute()}/db.sqlite3"
25-
sys.path.append(f"{PROJECT_DIR}")
22+
sys.path.append(f"{CURRENT_DIR.parent.parent}")
23+
db = DB(
24+
url=make_url(f"sqlite+aiosqlite:///{CURRENT_DIR.absolute()}/db.sqlite3"),
25+
)
2626

27-
Base = declarative_base()
27+
28+
class Base(DeclarativeBase):
29+
pass
2830

2931

3032
class User(Base):
3133
__tablename__ = "users"
3234

33-
id = Column(Integer, primary_key=True, autoincrement=False)
34-
name = Column(Text, nullable=True)
35+
id: Mapped[int] = mapped_column(primary_key=True)
36+
name: Mapped[Optional[str]]
3537

3638

3739
class UserAttributesBaseSchema(BaseModel):
@@ -56,20 +58,8 @@ class UserInSchema(UserAttributesBaseSchema):
5658
id: Annotated[int, ClientCanSetId()]
5759

5860

59-
async def get_session():
60-
sess = sessionmaker(
61-
bind=create_async_engine(url=make_url(DB_URL)),
62-
class_=AsyncSession,
63-
expire_on_commit=False,
64-
)
65-
async with sess() as db_session: # type: AsyncSession
66-
yield db_session
67-
await db_session.rollback()
68-
69-
7061
async def sqlalchemy_init() -> None:
71-
engine = create_async_engine(url=make_url(DB_URL))
72-
async with engine.begin() as conn:
62+
async with db.engine.begin() as conn:
7363
await conn.run_sync(Base.metadata.create_all)
7464

7565

@@ -78,7 +68,7 @@ class SessionDependency(BaseModel):
7868
arbitrary_types_allowed=True,
7969
)
8070

81-
session: AsyncSession = Depends(get_session)
71+
session: AsyncSession = Depends(db.session)
8272

8373

8474
def session_dependency_handler(view: ViewBase, dto: SessionDependency) -> dict:
@@ -145,16 +135,17 @@ def create_app() -> FastAPI:
145135
)
146136
add_routes(app)
147137
app.on_event("startup")(sqlalchemy_init)
138+
app.on_event("shutdown")(db.dispose)
148139
init(app)
149140
return app
150141

151142

152143
app = create_app()
153144

145+
154146
if __name__ == "__main__":
155-
current_file_name = CURRENT_FILE.name.replace(CURRENT_FILE.suffix, "")
156147
uvicorn.run(
157-
f"{current_file_name}:app",
148+
f"{CURRENT_FILE.name.replace(CURRENT_FILE.suffix, '')}:app",
158149
host="0.0.0.0",
159150
port=8084,
160151
reload=True,
Lines changed: 20 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,36 @@
1-
from sqlalchemy import Column, Integer, String, ForeignKey
2-
from sqlalchemy.orm import relationship
1+
from __future__ import annotations
32

4-
from examples.api_for_sqlalchemy.extensions.sqlalchemy import Base
5-
from examples.api_for_sqlalchemy.utils.sqlalchemy.base_model_mixin import BaseModelMixin
3+
from typing import Optional
64

5+
from sqlalchemy import ForeignKey
6+
from sqlalchemy.orm import relationship, Mapped, mapped_column
77

8-
class User(Base, BaseModelMixin):
8+
from examples.api_for_sqlalchemy.models.base import Base
9+
10+
11+
class User(Base):
912
__tablename__ = "users"
1013

11-
id = Column(Integer, primary_key=True, autoincrement=True)
12-
name = Column(String)
14+
name: Mapped[str]
1315

14-
posts = relationship("Post", back_populates="user", uselist=True)
15-
bio = relationship("UserBio", back_populates="user", uselist=False)
16-
computers = relationship("Computer", back_populates="user", uselist=True)
16+
bio: Mapped[UserBio] = relationship(back_populates="user")
17+
computers: Mapped[list[Computer]] = relationship(back_populates="user")
1718

1819

19-
class Computer(Base, BaseModelMixin):
20+
class Computer(Base):
2021
__tablename__ = "computers"
2122

22-
id = Column(Integer, primary_key=True, autoincrement=True)
23-
name = Column(String, nullable=False)
23+
name: Mapped[str]
2424

25-
user_id = Column(Integer, ForeignKey("users.id"), nullable=True)
26-
user = relationship("User", back_populates="computers")
25+
user_id: Mapped[Optional[int]] = mapped_column(ForeignKey("users.id"))
26+
user: Mapped[User] = relationship(back_populates="computers")
2727

2828

29-
class UserBio(Base, BaseModelMixin):
29+
class UserBio(Base):
3030
__tablename__ = "user_bio"
3131

32-
id = Column(Integer, primary_key=True, autoincrement=True)
33-
birth_city = Column(String, nullable=False, default="", server_default="")
34-
favourite_movies = Column(String, nullable=False, default="", server_default="")
32+
birth_city: Mapped[str] = mapped_column(default="", server_default="")
33+
favourite_movies: Mapped[str] = mapped_column(default="", server_default="")
3534

36-
user_id = Column(Integer, ForeignKey("users.id"), nullable=False, unique=True)
37-
user = relationship("User", back_populates="bio", uselist=False)
35+
user_id: Mapped[int] = mapped_column(ForeignKey("users.id"), unique=True)
36+
user: Mapped[User] = relationship(back_populates="bio")

docs/python_snippets/relationships/relationships_info_example.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@
22

33
from typing import Optional, Annotated
44

5-
from pydantic import BaseModel
6-
from pydantic import ConfigDict
5+
from pydantic import BaseModel, ConfigDict
76

87
from fastapi_jsonapi.types_metadata import RelationshipInfo
98

docs/python_snippets/routing/router.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,7 @@
11
from fastapi import APIRouter, FastAPI
22

33
from examples.api_for_sqlalchemy.models import User
4-
from examples.api_for_sqlalchemy.schemas import (
5-
UserInSchema,
6-
UserPatchSchema,
7-
UserSchema,
8-
)
4+
from examples.api_for_sqlalchemy.schemas import UserInSchema, UserPatchSchema, UserSchema
95
from fastapi_jsonapi import RoutersJSONAPI
106
from fastapi_jsonapi.misc.sqla.generics.base import DetailViewBase, ListViewBase
117

docs/python_snippets/view_dependencies/main_example.py

Lines changed: 12 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,27 @@
1-
from __future__ import annotations
2-
3-
from typing import ClassVar
1+
from typing import Optional, ClassVar
42

53
from fastapi import Depends, Header
64
from pydantic import BaseModel, ConfigDict
7-
from sqlalchemy.engine import make_url
85
from sqlalchemy.ext.asyncio import AsyncSession
9-
from sqlalchemy.ext.asyncio import create_async_engine
10-
from sqlalchemy.orm import sessionmaker
11-
from typing_extensions import Annotated, Optional
6+
from typing_extensions import Annotated
127

8+
from examples.api_for_sqlalchemy.models.db import DB
139
from fastapi_jsonapi.exceptions import Forbidden
14-
from fastapi_jsonapi.misc.sqla.generics.base import (
15-
DetailViewBaseGeneric,
16-
ListViewBaseGeneric,
17-
)
18-
from fastapi_jsonapi.views.utils import (
19-
HTTPMethod,
20-
HTTPMethodConfig,
21-
)
10+
from fastapi_jsonapi.misc.sqla.generics.base import DetailViewBaseGeneric, ListViewBaseGeneric
11+
from fastapi_jsonapi.views.utils import HTTPMethod, HTTPMethodConfig
2212
from fastapi_jsonapi.views.view_base import ViewBase
2313

24-
25-
def get_async_sessionmaker() -> sessionmaker:
26-
return sessionmaker(
27-
bind=create_async_engine(
28-
url=make_url(
29-
f"sqlite+aiosqlite:///tmp/db.sqlite3",
30-
)
31-
),
32-
class_=AsyncSession,
33-
expire_on_commit=False,
34-
)
35-
36-
37-
async def async_session_dependency():
38-
"""
39-
Get session as dependency
40-
41-
:return:
42-
"""
43-
session_maker = get_async_sessionmaker()
44-
async with session_maker() as db_session: # type: AsyncSession
45-
yield db_session
46-
await db_session.rollback()
14+
db = DB(
15+
url="sqlite+aiosqlite:///tmp/db.sqlite3",
16+
)
4717

4818

4919
class SessionDependency(BaseModel):
5020
model_config = ConfigDict(
5121
arbitrary_types_allowed=True,
5222
)
5323

54-
session: AsyncSession = Depends(async_session_dependency)
24+
session: AsyncSession = Depends(db.session)
5525

5626

5727
async def common_handler(view: ViewBase, dto: SessionDependency) -> dict:
@@ -80,7 +50,9 @@ class DetailView(DetailViewBaseGeneric):
8050

8151
class ListView(ListViewBaseGeneric):
8252
method_dependencies: ClassVar[dict[HTTPMethod, HTTPMethodConfig]] = {
83-
HTTPMethod.GET: HTTPMethodConfig(dependencies=AdminOnlyPermission),
53+
HTTPMethod.GET: HTTPMethodConfig(
54+
dependencies=AdminOnlyPermission,
55+
),
8456
HTTPMethod.ALL: HTTPMethodConfig(
8557
dependencies=SessionDependency,
8658
prepare_data_layer_kwargs=common_handler,

docs/requirements.txt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
fastapi>0.100.0
2-
orjson>=3.10.15
3-
pydantic>=2
2+
orjson>=3.2.1
3+
pydantic>=2.6.0
44
sphinx
55
sphinx_rtd_theme
6-
sqlalchemy<2
6+
sqlalchemy>=2.0.26

0 commit comments

Comments
 (0)