Skip to content

Commit b2c4be4

Browse files
author
h0rn3t
committed
support sqlalchemy from 1.4.18 to 2.0+, fix #12
1 parent 230b991 commit b2c4be4

File tree

6 files changed

+32
-40
lines changed

6 files changed

+32
-40
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ Provides SQLAlchemy middleware for FastAPI using AsyncSession and async engine.
2020

2121
### Important !!!
2222
If you use ```sqlmodel``` install ```fastapi-async-sqlalchemy==0.3.14```, because it depends on ```sqlalchemy<=1.4.41```
23-
```bash
23+
```last version``` compatible with ```sqlmodel``` sqlalchemy>=2.0.0 but if you will use ```sqlalchemy>=1.4.42``` and ```sqlmodel``` you will get error
24+
```bash
2425
pip install fastapi-async-sqlalchemy==0.3.14
2526
```
2627

fastapi_async_sqlalchemy/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22

33
__all__ = ["db", "SQLAlchemyMiddleware"]
44

5-
__version__ = "0.4.2"
5+
__version__ = "0.5.0"

fastapi_async_sqlalchemy/middleware.py

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,13 @@
33

44
from sqlalchemy.engine import Engine
55
from sqlalchemy.engine.url import URL
6-
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine
6+
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
7+
8+
try:
9+
from sqlalchemy.ext.asyncio import async_sessionmaker
10+
except ImportError:
11+
from sqlalchemy.orm import sessionmaker as async_sessionmaker
12+
713
from starlette.middleware.base import BaseHTTPMiddleware, RequestResponseEndpoint
814
from starlette.requests import Request
915
from starlette.types import ASGIApp
@@ -37,16 +43,16 @@ def __init__(
3743
engine = custom_engine
3844

3945
global _Session
40-
_Session = async_sessionmaker(engine, expire_on_commit=False, **session_args)
46+
_Session = async_sessionmaker(
47+
engine, class_=AsyncSession, expire_on_commit=False, **session_args
48+
)
4149

4250
async def dispatch(self, request: Request, call_next: RequestResponseEndpoint):
4351
async with db(commit_on_exit=self.commit_on_exit):
4452
return await call_next(request)
4553

4654

4755
class DBSessionMeta(type):
48-
# using this metaclass means that we can access db.session as a property at a class level,
49-
# rather than db().session
5056
@property
5157
def session(self) -> AsyncSession:
5258
"""Return an instance of Session local to the current async context."""
@@ -75,14 +81,15 @@ async def __aenter__(self):
7581

7682
async def __aexit__(self, exc_type, exc_value, traceback):
7783
session = _session.get()
78-
if exc_type is not None:
79-
await session.rollback()
80-
81-
if self.commit_on_exit:
82-
await session.commit()
8384

84-
await session.close()
85-
_session.reset(self.token)
85+
try:
86+
if exc_type is not None:
87+
await session.rollback()
88+
elif self.commit_on_exit: # Note: Changed this to elif to avoid commit after rollback
89+
await session.commit()
90+
finally:
91+
await session.close()
92+
_session.reset(self.token)
8693

8794

8895
db: DBSessionMeta = DBSession

requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ regex==2020.2.20
2727
requests>=2.22.0
2828
httpx>=0.20.0
2929
six==1.12.0
30-
SQLAlchemy>=2.0.0
30+
SQLAlchemy>=1.4.19
3131
asyncpg>=0.27.0
3232
aiosqlite==0.19.0
3333
sqlparse==0.4.4

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
package_data={"fastapi_async_sqlalchemy": ["py.typed"]},
2828
zip_safe=False,
2929
python_requires=">=3.7",
30-
install_requires=["starlette>=0.13.6", "SQLAlchemy>=2.0.0"],
30+
install_requires=["starlette>=0.13.6", "SQLAlchemy>=1.4.19"],
3131
classifiers=[
3232
"Development Status :: 4 - Beta",
3333
"Environment :: Web Environment",

tests/test_session.py

Lines changed: 9 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import sys
33

44
import pytest
5+
from sqlalchemy.exc import IntegrityError
56
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
67
from starlette.middleware.base import BaseHTTPMiddleware
78

@@ -119,31 +120,14 @@ async def test_init_session(app, db, SQLAlchemyMiddleware):
119120
assert isinstance(db.session, AsyncSession)
120121

121122

122-
@pytest.mark.parametrize("commit_on_exit", [True, False])
123123
@pytest.mark.asyncio
124-
async def test_db_context_session_args(app, db, SQLAlchemyMiddleware, commit_on_exit):
125-
app.add_middleware(SQLAlchemyMiddleware, db_url=db_url, commit_on_exit=commit_on_exit)
124+
async def test_db_session_commit_fail(app, db, SQLAlchemyMiddleware):
125+
app.add_middleware(SQLAlchemyMiddleware, db_url=db_url, commit_on_exit=True)
126126

127-
session_args = {}
128-
129-
async with db(session_args=session_args, commit_on_exit=True):
130-
assert isinstance(db.session, AsyncSession)
131-
132-
# assert db.session.expire_on_commit
133-
134-
session_args = {"expire_on_commit": False}
135-
async with db(session_args=session_args):
136-
db.session
137-
# assert db.session.expire_on_commit
138-
139-
140-
@pytest.mark.asyncio
141-
async def test_rollback(app, db, SQLAlchemyMiddleware):
142-
# pytest-cov shows that the line in db.__exit__() rolling back the db session
143-
# when there is an Exception is run correctly. However, it would be much better
144-
# if we could demonstrate somehow that db.session.rollback() was called e.g. once
145-
app.add_middleware(SQLAlchemyMiddleware, db_url=db_url)
146-
147-
with pytest.raises(Exception):
127+
with pytest.raises(IntegrityError):
148128
async with db():
149-
raise Exception
129+
raise IntegrityError("test", "test", "test")
130+
db.session.close.assert_called_once()
131+
132+
async with db():
133+
assert db.session

0 commit comments

Comments
 (0)