You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
*[](https://github.com/mkenney/software-guides/blob/master/STABILITY-BADGES.md#beta)**SQLAlchemy manager:** Implementation is mostly finalised, needs testing in production.
33
-
*[](https://github.com/mkenney/software-guides/blob/master/STABILITY-BADGES.md#work-in-progress)**Repository / Unit of work:**Major work is stil necessary to finalise the interface, to hide the session management implementation details from the application.
33
+
*[](https://github.com/mkenney/software-guides/blob/master/STABILITY-BADGES.md#beta)**Repository / Unit of work:**Implementation is mostly finalised, needs testing in production.
o = ImperativeModel() # also o = DeclarativeModel()
100
+
o = ImperativeModel() # also o = DeclarativeModel()
98
101
o.name ="John"
99
102
with sa_manager.get_bind().session_class()() as session:
100
103
session.add(o)
@@ -209,47 +212,52 @@ A `Repository` represents a generic interface to persist data object to a storag
209
212
using SQLAlchemy. It makes sense that the lifecycle of a `Session` follows the one of the Repository
210
213
(If we create a Repository, we're going to do a DB operation, otherwise we don't need a `Session`)
211
214
212
-
This ensures we the `Session` we use is isolated, and the same for all the operations we do with the
213
-
repository.
215
+
This ensures the `Session` we use is isolated, and the same for all the operations we do with the
216
+
same repository.
214
217
215
-
The consequence of this choice is we can't use SQLAlchemy lazy loading, so we need to make sure
216
-
relationship are loaded eagerly. You can do this by:
218
+
The session is automatically closed and reopen with each Repository operation, this make sure these
219
+
operation are independent from each other.
217
220
218
-
* Setup your model/table relationships to always use always eager loading
219
-
* Implement ad-hoc methods to deal with relationships as necessary
221
+
These choices cause some consequences:
222
+
* The operations that modify the database will reload the models from the DB, causing an additional
223
+
SELECT query to be issued.
224
+
* We can't use SQLAlchemy lazy loading, so we'll need to make sure relationship are always loaded eagerly,
225
+
using either:
226
+
* Setup your model/table relationships to always use always eager loading
227
+
* Implement ad-hoc methods to deal with relationships as necessary
220
228
221
229
Also `AsyncSession` has [the same limitation on lazy loading](https://docs.sqlalchemy.org/en/20/orm/extensions/asyncio.html#asyncio-orm-avoid-lazyloads)
222
230
so it makes sense that the two repository implementations behave consistently.
223
231
224
-
### Use the Unit Of Work to share a session among multiple repositories[alpha]
232
+
### Use the Unit Of Work to share a session among multiple repositories
225
233
226
-
It is possible we need to run several operations in a single database transaction. While a single
234
+
It is possible we need to run several operations in a single database get_session. While a single
227
235
repository provide by itself an isolated session for single operations, we have to use a different
228
236
approach for multiple operations.
229
237
230
-
We can use the `SQLAlchemyUnitOfWork` or the `SQLAlchemyUnitOfWork` class to provide a shared session to
238
+
We can use the `UnitOfWork` or the `AsyncUnitOfWork` class to provide a shared session to
231
239
be used for repository operations, **assumed the same bind is used for all the repositories**.
232
240
(Two phase transactions are not currently supported).
233
241
234
-
All repositories operation methods accept the `session` parameter for this purpose. This makes the
235
-
operations to bypass the internal repository-managed session.
236
-
237
242
```python
243
+
classMyRepo(SQLAlchemyRepository):
244
+
_model = MyModel
245
+
classMyOtherRepo(SQLAlchemyRepository):
246
+
_model = MyOtherModel
247
+
238
248
bind = sa_manager.get_bind()
239
-
repo1 = MyRepo(bind)
240
-
repo2 = MyOtherRepo(bind)
241
-
uow = SQLAlchemyUnitOfWork(bind)
249
+
uow = UnitOfWork(bind, (MyRepo, MyOtherRepo))
242
250
243
-
with uow.get_session() as _session:
244
-
repo1.save(some_model, session=_session)
245
-
repo2.save(some_model, session=_session)
251
+
with uow.transaction():
252
+
uow.MyRepo.save(some_model)
253
+
uow.MyOtherRepo.save(some_other_model)
246
254
247
255
# Optionally disable the commit/rollback handling
248
-
with uow.get_session(commit=False) as _session:
249
-
model1 =repo1.get(1, session=_session)
250
-
model2 =repo1.get(1, session=_session)
256
+
with uow.transaction(read_only=True):
257
+
model1 =uow.MyRepo.get(1)
258
+
model2 =uow.MyOtherRepo.get(2)
251
259
```
252
260
253
261
Both the UnitOfWork classes create an internal `scoped_session` or `async_scoped_session`, behaving
254
262
in the same way at the repositories do. This provides the freedom to tune the session lifecycle based
255
-
on our application requirements (e.g. one session per http request, per domain, etc.)
263
+
on our application requirements (e.g. one unit of work per http request, per domain, etc.)
0 commit comments