Skip to content

Commit d29d158

Browse files
authored
feat: add base entity class with deferred reflection (#7)
1 parent 6e4216f commit d29d158

5 files changed

Lines changed: 67 additions & 8 deletions

File tree

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,10 @@ SqlAlchemy integration for FastAPI®
2222
app = FastAPI()
2323
fastapi_sqla.setup(app)
2424
```
25+
* Adding a new entity class:
26+
```python
27+
from fastapi_sqla import Base
28+
29+
class Entity(Base):
30+
__tablename__ = "table-name-in-db"
31+
```

fastapi_sqla.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@
33
import structlog
44
from fastapi import FastAPI
55
from sqlalchemy import engine_from_config
6+
from sqlalchemy.ext.declarative import DeferredReflection, declarative_base
67
from sqlalchemy.orm.session import sessionmaker
78

8-
__all__ = ["setup"]
9+
__all__ = ["Base", "setup"]
910

1011
logger = structlog.get_logger(__name__)
1112

@@ -18,5 +19,11 @@ def setup(app: FastAPI):
1819

1920
def startup():
2021
engine = engine_from_config(os.environ, prefix="sqlalchemy_")
22+
Base.metadata.bind = engine
23+
Base.prepare(engine)
2124
_Session.configure(bind=engine)
2225
logger.info("startup", engine=engine)
26+
27+
28+
class Base(declarative_base(cls=DeferredReflection)): # type: ignore
29+
__abstract__ = True

tests/conftest.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import importlib
12
import os
23
from unittest.mock import patch
34

@@ -13,5 +14,13 @@ def db_uri():
1314
@fixture(autouse=True)
1415
def environ(db_uri):
1516
values = {"sqlalchemy_url": db_uri}
16-
with patch.dict("os.environ", values=values, clear=True) as environ:
17-
yield environ
17+
with patch.dict("os.environ", values=values, clear=True):
18+
yield values
19+
20+
21+
@fixture(autouse=True)
22+
def tear_down():
23+
import fastapi_sqla
24+
25+
# reload fastapi_sqla to clear sqla deferred reflection mapping stored in Base
26+
importlib.reload(fastapi_sqla)

tests/test_base.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
from pytest import fixture, raises
2+
from sqlalchemy import engine_from_config
3+
from sqlalchemy.exc import NoSuchTableError
4+
from sqlalchemy.orm.session import close_all_sessions
5+
6+
7+
@fixture(autouse=True)
8+
def setup_tear_down(environ):
9+
engine = engine_from_config(environ, prefix="sqlalchemy_")
10+
11+
engine.execute("CREATE TABLE IF NOT EXISTS test_table (id integer primary key)")
12+
yield
13+
close_all_sessions()
14+
engine.execute("DROP TABLE test_table")
15+
16+
17+
def test_startup_reflect_test_table():
18+
from fastapi_sqla import Base, _Session, startup
19+
20+
class TestTable(Base):
21+
__tablename__ = "test_table"
22+
23+
startup()
24+
25+
session = _Session()
26+
session.add(TestTable(id=1))
27+
session.add(TestTable(id=2))
28+
session.commit()
29+
30+
assert session.query(TestTable).count() == 2
31+
32+
33+
def test_startup_fails_when_table_doesnt_exist():
34+
from fastapi_sqla import Base, startup
35+
36+
class TestTable(Base):
37+
__tablename__ = "does_not_exist"
38+
39+
with raises(NoSuchTableError):
40+
startup()

tests/test_startup.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
11
import httpx
22
from asgi_lifespan import LifespanManager
3+
from fastapi import FastAPI
34
from pytest import fixture, mark
45
from sqlalchemy.orm.session import close_all_sessions
56

67

78
@fixture(autouse=True)
89
def setup_tear_down():
9-
from fastapi_sqla import _Session
10-
1110
yield
12-
_Session.configure(bind=None)
1311
close_all_sessions()
1412

1513

@@ -25,8 +23,6 @@ def test_startup():
2523

2624
@mark.asyncio
2725
async def test_fastapi_integration():
28-
from fastapi import FastAPI
29-
3026
from fastapi_sqla import _Session, setup
3127

3228
app = FastAPI()

0 commit comments

Comments
 (0)