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.
34
-
*[](https://github.com/mkenney/software-guides/blob/master/STABILITY-BADGES.md#beta)**Repository / Unit of work:** Implementation is mostly finalised, needs testing in production.
34
+
*[](https://github.com/mkenney/software-guides/blob/master/STABILITY-BADGES.md#beta)**Repository:** Implementation is mostly finalised, needs testing in production.
35
+
*[](https://github.com/mkenney/software-guides/blob/master/STABILITY-BADGES.md#experimental)**Unit of work:** The implementation is working but limited to repositories using the same engine. Distributed transactions across different engines are not yet supported.
35
36
37
+
## Documentation
38
+
39
+
The complete documentation can be found [here](https://febus982.github.io/sqlalchemy-bind-manager)
36
40
37
41
## SQLAlchemy manager
38
42
@@ -50,24 +54,16 @@ config = SQLAlchemyConfig(
50
54
sa_manager = SQLAlchemyBindManager(config)
51
55
```
52
56
53
-
🚨 NOTE: Using global variables is not thread-safe, please read the [Threading](#threading) section if your application uses multi-threading.
57
+
🚨 NOTE: Using global variables is not thread-safe, please read the [Documentation](https://febus982.github.io/sqlalchemy-bind-manager/manager/session/#note-on-multithreaded-applications) if your application uses multi-threading.
54
58
55
59
The `engine_url` and `engine_options` dictionaries accept the same parameters as SQLAlchemy [create_engine()](https://docs.sqlalchemy.org/en/14/core/engines.html#sqlalchemy.create_engine)
56
60
57
61
The `session_options` dictionary accepts the same parameters as SQLALchemy [sessionmaker()](https://docs.sqlalchemy.org/en/14/orm/session_api.html#sqlalchemy.orm.sessionmaker)
58
62
59
-
Once the bind manager is initialised we can retrieve and use the SQLAlchemyBind using the method `get_bind()`
60
-
61
-
The `SQLAlchemyBind` and `SQLAlchemyAsyncBind` class has the following attributes:
62
-
63
-
*`engine`: The initialised SQLALchemy `Engine`
64
-
*`model_declarative_base`: A base class that can be used to create [declarative models](https://docs.sqlalchemy.org/en/14/orm/mapping_styles.html#declarative-mapping)
65
-
*`registry_mapper`: The `registry` associated with the `engine`. It can be used with Alembic or to setup [imperative mapping](https://docs.sqlalchemy.org/en/14/orm/mapping_styles.html#imperative-mapping)
66
-
*`session_class`: The class built by [sessionmaker()](https://docs.sqlalchemy.org/en/14/orm/session_api.html#sqlalchemy.orm.sessionmaker), either `Session` or `AsyncSession`
63
+
The `SQLAlchemyBindManager` provides some helper methods for common operations:
67
64
68
-
The `SQLAlchemyBindManager` provides some helper methods to quickly access some of the bind properties without using the `SQLAlchemyBind`:
69
-
70
-
*`get_session`: returns a Session object
65
+
*`get_bind`: returns a `SQLAlchemyBind` or `SQLAlchemyAsyncBind` object
66
+
*`get_session`: returns a `Session` object, which works also as a context manager
71
67
*`get_mapper`: returns the mapper associated with the bind
o =ImperativeModel() # also o = DeclarativeModel()
80
+
o =MyModel()
101
81
o.name ="John"
102
-
with sa_manager.get_bind().session_class()() as session:
103
-
session.add(o)
104
-
session.commit()
105
-
106
-
# or using the get_session() helper method for better readability
107
82
with sa_manager.get_session() as session:
108
83
session.add(o)
109
84
session.commit()
110
-
111
85
```
112
86
87
+
[Imperative model declaration](https://febus982.github.io/sqlalchemy-bind-manager/manager/models/) is also supported.
88
+
113
89
### Multiple database binds
114
90
115
91
`SQLAlchemyBindManager` accepts also multiple databases configuration, provided as a dictionary. The dictionary keys are used as a reference name for each bind.
recommends we create `Session` object at the beginning of a logical operation where
228
-
database access is potentially anticipated.
229
-
230
-
Doing this too soon might cause unexpected effects, like unexpected updates being committed,
231
-
if the initialised session is shared among different repositories.
232
-
233
-
A `Repository` represents a generic interface to persist data object to a storage, not necessarily
234
-
using SQLAlchemy. It makes sense that the lifecycle of a `Session` follows the one of the Repository
235
-
(The assumption is: if we create a Repository, we're going to do a DB operation,
236
-
otherwise we wouldn't need one).
237
-
238
-
Each Repository instance create an internal scoped session. The session gets
239
-
automatically closed when the Repository instance is not referenced by any variable (and the
240
-
garbage collector clean it up)
241
-
242
-
In this way we ensure the `Session` we use is isolated, and the same for all the operations we do with the
243
-
same Repository.
244
-
245
-
This approach has a consequence: We can't use SQLAlchemy lazy loading, so we'll need to make sure relationship are always loaded eagerly,
246
-
using either approach:
247
-
* Setup your model/table relationships to always use always eager loading
248
-
* Implement ad-hoc methods to deal with relationships as necessary
249
-
250
-
Note that `AsyncSession` has [the same limitation on lazy loading](https://docs.sqlalchemy.org/en/20/orm/extensions/asyncio.html#asyncio-orm-avoid-lazyloads),
251
-
even when keeping the session opened, so it makes sense that the two Repository implementations behave consistently.
252
-
253
173
### Use the Unit Of Work to share a session among multiple repositories
254
174
255
175
It is possible we need to run several operations in a single database transaction. While a single
0 commit comments