66
77
88class FastAPISessionMaker :
9+ """
10+ This class provides a convenient cached interface for accessing sqlalchemy ORM sessions using just a database URI.
11+
12+ The expected format of database_uri is `"<scheme>://<user>:<password>@<host>:<port>/<database>`, exactly as you'd
13+ use with `sqlalchemy.create_engine`.
14+
15+ For example, if a postgres database named `app` is accessible on a container named `db`,
16+ the `database_uri` might look like: "postgresql://db_user:password@db:5432/app"
17+ """
18+
919 def __init__ (self , database_uri : str ):
1020 self .database_uri = database_uri
1121
@@ -14,6 +24,9 @@ def __init__(self, database_uri: str):
1424
1525 @property
1626 def cached_engine (self ) -> sa .engine .Engine :
27+ """
28+ Returns the cached engine if present, or a new (cached) engine using the database_uri if not
29+ """
1730 engine = self ._cached_engine
1831 if engine is None :
1932 engine = self .get_new_engine ()
@@ -22,49 +35,99 @@ def cached_engine(self) -> sa.engine.Engine:
2235
2336 @property
2437 def cached_sessionmaker (self ) -> sa .orm .sessionmaker :
38+ """
39+ Returns the cached sessionmaker if present, or a new (cached) sessionmaker using the cached_engine if not
40+ """
2541 sessionmaker = self ._cached_sessionmaker
2642 if sessionmaker is None :
2743 sessionmaker = self .get_new_sessionmaker (self .cached_engine )
2844 self ._cached_sessionmaker = sessionmaker
2945 return sessionmaker
3046
31- def get_new_engine (self ,) -> sa .engine .Engine :
47+ def get_new_engine (self ) -> sa .engine .Engine :
48+ """
49+ Returns a new sqlalchemy engine for the database_uri.
50+ """
3251 return get_engine (self .database_uri )
3352
3453 def get_new_sessionmaker (self , engine : Optional [sa .engine .Engine ]) -> sa .orm .sessionmaker :
54+ """
55+ Returns a new sessionmaker for the (optional) provided engine.
56+
57+ If `None` is provided, the cached engine is used. (A new engine is created and cached if necessary.)
58+ """
3559 engine = engine or self .cached_engine
3660 return get_sessionmaker_for_engine (engine )
3761
3862 def get_db (self ) -> Iterator [Session ]:
3963 """
40- Intended for use as a FastAPI dependency
64+ A FastAPI dependency that yields a sqlalchemy session.
65+
66+ The session is created by the cached sessionmaker, and closed via contextmanager after the response is returned.
67+
68+ Note that if you perform any database writes and want to handle errors *prior* to returning a response (and you
69+ should!), you'll need to put `session.commit()` or `session.rollback()` as appropriate in your endpoint code.
70+ This is generally a best practice for expected errors anyway since otherwise you would generate a 500 response.
4171 """
4272 yield from _get_db (self .cached_sessionmaker )
4373
4474 @contextmanager
4575 def context_session (self ) -> Iterator [Session ]:
76+ """
77+ This method directly produces a context-managed session without relying on FastAPI's dependency injection.
78+
79+ Usage would look like:
80+ ```python
81+ session_maker = FastAPISessionMaker(db_uri)
82+ with session_maker.context_session() as session:
83+ instance = session.query(OrmModel).get(instance_id)
84+ ```
85+ """
4686 yield from self .get_db ()
4787
4888 def reset_cache (self ) -> None :
89+ """
90+ Resets the engine and sessionmaker caches.
91+
92+ After calling this method, the next time you try to use the cached engine or sessionmaker,
93+ new ones will be created.
94+ """
4995 self ._cached_engine = None
5096 self ._cached_sessionmaker = None
5197
5298
5399def get_engine (uri : str ) -> sa .engine .Engine :
100+ """
101+ Returns a new sqlalchemy engine that "tests connections for liveness upon each checkout".
102+ """
54103 return sa .create_engine (uri , pool_pre_ping = True )
55104
56105
57106def get_sessionmaker_for_engine (engine : sa .engine .Engine ) -> sa .orm .sessionmaker :
107+ """
108+ Returns a sqlalchemy sessionmaker for the provided engine, using recommended settings for use with FastAPI.
109+ """
58110 return sa .orm .sessionmaker (autocommit = False , autoflush = False , bind = engine )
59111
60112
61113@contextmanager
62114def context_session (engine : sa .engine .Engine ) -> Iterator [Session ]:
115+ """
116+ This method produces a context-managed session for use with a specified engine.
117+
118+ Behaves similarly to FastAPISessionMaker.context_session.
119+ """
63120 sessionmaker = get_sessionmaker_for_engine (engine )
64121 yield from _get_db (sessionmaker )
65122
66123
67124def _get_db (sessionmaker : sa .orm .sessionmaker ) -> Iterator [Session ]:
125+ """
126+ The underlying generator function used to create context-managed sqlalchemy sessions for:
127+ * context_session
128+ * FastAPISessionMaker.context_session
129+ * FastAPISessionMaker.get_db
130+ """
68131 session = sessionmaker ()
69132 try :
70133 yield session
0 commit comments