activemodel 0.10.0__py3-none-any.whl → 0.12.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- activemodel/base_model.py +25 -9
- activemodel/cli/__init__.py +147 -0
- activemodel/mixins/timestamps.py +4 -1
- activemodel/mixins/typeid.py +10 -2
- activemodel/pytest/__init__.py +1 -1
- activemodel/pytest/factories.py +102 -0
- activemodel/pytest/plugin.py +81 -0
- activemodel/pytest/transaction.py +103 -16
- activemodel/pytest/truncate.py +108 -7
- activemodel/query_wrapper.py +12 -2
- activemodel/session_manager.py +64 -14
- activemodel/types/sqlalchemy_protocol.py +10 -0
- activemodel/types/sqlalchemy_protocol.pyi +132 -0
- activemodel/types/typeid.py +36 -22
- activemodel/types/typeid_patch.py +22 -0
- activemodel/utils.py +3 -39
- {activemodel-0.10.0.dist-info → activemodel-0.12.0.dist-info}/METADATA +40 -16
- activemodel-0.12.0.dist-info/RECORD +30 -0
- activemodel-0.12.0.dist-info/entry_points.txt +2 -0
- activemodel-0.10.0.dist-info/RECORD +0 -24
- activemodel-0.10.0.dist-info/entry_points.txt +0 -2
- {activemodel-0.10.0.dist-info → activemodel-0.12.0.dist-info}/WHEEL +0 -0
- {activemodel-0.10.0.dist-info → activemodel-0.12.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: activemodel
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.12.0
|
|
4
4
|
Summary: Make SQLModel more like an a real ORM
|
|
5
5
|
Project-URL: Repository, https://github.com/iloveitaly/activemodel
|
|
6
6
|
Author-email: Michael Bianco <iloveitaly@gmail.com>
|
|
7
7
|
License-File: LICENSE
|
|
8
8
|
Keywords: activemodel,activerecord,orm,sqlalchemy,sqlmodel
|
|
9
9
|
Requires-Python: >=3.10
|
|
10
|
-
Requires-Dist: pydash>=8.0.4
|
|
11
10
|
Requires-Dist: python-decouple-typed>=3.11.0
|
|
12
11
|
Requires-Dist: sqlmodel>=0.0.22
|
|
12
|
+
Requires-Dist: textcase>=0.4.0
|
|
13
13
|
Requires-Dist: typeid-python>=0.3.1
|
|
14
14
|
Description-Content-Type: text/markdown
|
|
15
15
|
|
|
@@ -59,15 +59,17 @@ from sqlmodel import SQLModel
|
|
|
59
59
|
|
|
60
60
|
SQLModel.metadata.create_all(get_engine())
|
|
61
61
|
|
|
62
|
-
# now you can create a user!
|
|
62
|
+
# now you can create a user! without managing sessions!
|
|
63
63
|
User(a_field="a").save()
|
|
64
64
|
```
|
|
65
65
|
|
|
66
66
|
Maybe you like JSON:
|
|
67
67
|
|
|
68
68
|
```python
|
|
69
|
-
from
|
|
69
|
+
from sqlalchemy.dialects.postgresql import JSONB
|
|
70
70
|
from pydantic import BaseModel as PydanticBaseModel
|
|
71
|
+
|
|
72
|
+
from activemodel import BaseModel
|
|
71
73
|
from activemodel.mixins import PydanticJSONMixin, TypeIDMixin, TimestampsMixin
|
|
72
74
|
|
|
73
75
|
class SubObject(PydanticBaseModel):
|
|
@@ -81,11 +83,28 @@ class User(
|
|
|
81
83
|
TypeIDMixin("user"),
|
|
82
84
|
table=True
|
|
83
85
|
):
|
|
84
|
-
list_field: list[SubObject] = Field(sa_type=JSONB
|
|
86
|
+
list_field: list[SubObject] = Field(sa_type=JSONB)
|
|
85
87
|
```
|
|
86
88
|
|
|
89
|
+
You'll probably want to query the model. Look ma, no sessions!
|
|
90
|
+
|
|
91
|
+
```python
|
|
92
|
+
User.where(id="user_123").all()
|
|
93
|
+
|
|
94
|
+
# or, even better, for this case
|
|
95
|
+
User.one("user_123")
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Magically creating sessions for DB operations is one of the main problems this project tackles. Even better, you can set
|
|
99
|
+
a single session object to be used for all DB operations. This is helpful for DB transactions, [specifically rolling back
|
|
100
|
+
DB operations on each test.](#pytest)
|
|
101
|
+
|
|
87
102
|
## Usage
|
|
88
103
|
|
|
104
|
+
### Pytest
|
|
105
|
+
|
|
106
|
+
TODO detail out truncation and transactions
|
|
107
|
+
|
|
89
108
|
### Integrating Alembic
|
|
90
109
|
|
|
91
110
|
`alembic init` will not work out of the box. You need to mutate a handful of files:
|
|
@@ -110,14 +129,14 @@ index 0d07420..a63631c 100644
|
|
|
110
129
|
# Use forward slashes (/) also on windows to provide an os agnostic path
|
|
111
130
|
-script_location = .
|
|
112
131
|
+script_location = migrations
|
|
113
|
-
|
|
132
|
+
|
|
114
133
|
# template used to generate migration file names; The default value is %%(rev)s_%%(slug)s
|
|
115
134
|
# Uncomment the line below if you want the files to be prepended with date and time
|
|
116
135
|
# see https://alembic.sqlalchemy.org/en/latest/tutorial.html#editing-the-ini-file
|
|
117
136
|
# for all available tokens
|
|
118
137
|
# file_template = %%(year)d_%%(month).2d_%%(day).2d_%%(hour).2d%%(minute).2d-%%(rev)s_%%(slug)s
|
|
119
138
|
+file_template = %%(year)d_%%(month).2d_%%(day).2d_%%(rev)s_%%(slug)s
|
|
120
|
-
|
|
139
|
+
|
|
121
140
|
# sys.path path, will be prepended to sys.path if present.
|
|
122
141
|
# defaults to the current working directory.
|
|
123
142
|
diff --git i/test/migrations/env.py w/test/migrations/env.py
|
|
@@ -129,12 +148,12 @@ index 36112a3..a1e15c2 100644
|
|
|
129
148
|
+# isort: off
|
|
130
149
|
+
|
|
131
150
|
from logging.config import fileConfig
|
|
132
|
-
|
|
151
|
+
|
|
133
152
|
from sqlalchemy import engine_from_config
|
|
134
153
|
@@ -14,11 +17,17 @@ config = context.config
|
|
135
154
|
if config.config_file_name is not None:
|
|
136
155
|
fileConfig(config.config_file_name)
|
|
137
|
-
|
|
156
|
+
|
|
138
157
|
+from sqlmodel import SQLModel
|
|
139
158
|
+from test.models import *
|
|
140
159
|
+from test.utils import database_url
|
|
@@ -147,7 +166,7 @@ index 36112a3..a1e15c2 100644
|
|
|
147
166
|
# target_metadata = mymodel.Base.metadata
|
|
148
167
|
-target_metadata = None
|
|
149
168
|
+target_metadata = SQLModel.metadata
|
|
150
|
-
|
|
169
|
+
|
|
151
170
|
# other values from the config, defined by the needs of env.py,
|
|
152
171
|
# can be acquired:
|
|
153
172
|
diff --git i/test/migrations/script.py.mako w/test/migrations/script.py.mako
|
|
@@ -155,13 +174,13 @@ index fbc4b07..9dc78bb 100644
|
|
|
155
174
|
--- i/test/migrations/script.py.mako
|
|
156
175
|
+++ w/test/migrations/script.py.mako
|
|
157
176
|
@@ -9,6 +9,8 @@ from typing import Sequence, Union
|
|
158
|
-
|
|
177
|
+
|
|
159
178
|
from alembic import op
|
|
160
179
|
import sqlalchemy as sa
|
|
161
180
|
+import sqlmodel
|
|
162
181
|
+import activemodel
|
|
163
182
|
${imports if imports else ""}
|
|
164
|
-
|
|
183
|
+
|
|
165
184
|
# revision identifiers, used by Alembic.
|
|
166
185
|
```
|
|
167
186
|
|
|
@@ -178,7 +197,7 @@ This tool is added to all `BaseModel`s and makes it easy to write SQL queries. S
|
|
|
178
197
|
|
|
179
198
|
### Easy Database Sessions
|
|
180
199
|
|
|
181
|
-
I hate the idea f
|
|
200
|
+
I hate the idea f
|
|
182
201
|
|
|
183
202
|
* Behavior should be intuitive and easy to understand. If you run `save()`, it should save, not stick the save in a transaction.
|
|
184
203
|
* Don't worry about dead sessions. This makes it easy to lazy-load computed properties and largely eliminates the need to think about database sessions.
|
|
@@ -186,7 +205,7 @@ I hate the idea f
|
|
|
186
205
|
There are a couple of thorny problems we need to solve for here:
|
|
187
206
|
|
|
188
207
|
* In-memory fastapi servers are not the same as a uvicorn server, which is threaded *and* uses some sort of threadpool model for handling async requests. I don't claim to understand the entire implementation. For global DB session state (a) we can't use global variables (b) we can't use thread-local variables.
|
|
189
|
-
*
|
|
208
|
+
*
|
|
190
209
|
|
|
191
210
|
https://github.com/tomwojcik/starlette-context
|
|
192
211
|
|
|
@@ -202,8 +221,12 @@ https://github.com/tomwojcik/starlette-context
|
|
|
202
221
|
SQLModel & SQLAlchemy are tricky. Here are some useful internal tricks:
|
|
203
222
|
|
|
204
223
|
* `__sqlmodel_relationships__` is where any `RelationshipInfo` objects are stored. This is used to generate relationship fields on the object.
|
|
205
|
-
* `ModelClass.relationship_name.property.local_columns`
|
|
224
|
+
* `ModelClass.relationship_name.property.local_columns`
|
|
206
225
|
* Get cached fields from a model `object_state(instance).dict.get(field_name)`
|
|
226
|
+
* Set the value on a field, without marking it as dirty `attributes.set_committed_value(instance, field_name, val)`
|
|
227
|
+
* Is a model dirty `instance_state(instance).modified`
|
|
228
|
+
* `select(Table).outerjoin??` won't work in a ipython session, but `Table.__table__.outerjoin??` will. `__table__` is a reference to the underlying SQLAlchemy table record.
|
|
229
|
+
* `get_engine().pool.stats()` is helpful for inspecting connection pools and limits\
|
|
207
230
|
|
|
208
231
|
### TypeID
|
|
209
232
|
|
|
@@ -267,6 +290,7 @@ https://github.com/DarylStark/my_data/blob/a17b8b3a8463b9953821b89fee895e272f94d
|
|
|
267
290
|
|
|
268
291
|
* https://github.com/woofz/sqlmodel-basecrud
|
|
269
292
|
* https://github.com/0xthiagomartins/sqlmodel-controller
|
|
293
|
+
* https://github.com/litestar-org/advanced-alchemy?tab=readme-ov-file
|
|
270
294
|
|
|
271
295
|
## Inspiration
|
|
272
296
|
|
|
@@ -279,4 +303,4 @@ https://github.com/DarylStark/my_data/blob/a17b8b3a8463b9953821b89fee895e272f94d
|
|
|
279
303
|
|
|
280
304
|
## Upstream Changes
|
|
281
305
|
|
|
282
|
-
- [ ] https://github.com/fastapi/sqlmodel/pull/1293
|
|
306
|
+
- [ ] https://github.com/fastapi/sqlmodel/pull/1293
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
activemodel/__init__.py,sha256=q_lHQyIM70ApvjduTo9GtenQjJXsfYZsAAquD_51kF4,137
|
|
2
|
+
activemodel/base_model.py,sha256=zku7nKOcN4_YpIpHjP3_sWeJWGTf141gUYtPPilY0MU,17359
|
|
3
|
+
activemodel/celery.py,sha256=L1vKcO_HoPA5ZCfsXjxgPpDUMYDuoQMakGA9rppN7Lo,897
|
|
4
|
+
activemodel/errors.py,sha256=wycWYmk9ws4TZpxvTdtXVy2SFESb8NqKgzdivBoF0vw,115
|
|
5
|
+
activemodel/get_column_from_field_patch.py,sha256=wAEDm_ZvSqyJwfgkXVpxsevw11hd-7VLy7zuJG8Ak7Y,4986
|
|
6
|
+
activemodel/logger.py,sha256=vU7QiGSy_AJuJFmClUocqIJ-Ltku_8C24ZU8L6fLJR0,53
|
|
7
|
+
activemodel/query_wrapper.py,sha256=gXpEAAFpRqQNUbTubTX-qkMNHeztFnyxtjdJbplCkKA,2867
|
|
8
|
+
activemodel/session_manager.py,sha256=Aqc96ByAgrcuRRPsMZiTN3tlSXUFZvM3U7N8vCO2TIk,6720
|
|
9
|
+
activemodel/utils.py,sha256=tZlAk0G46g6dwYuN7dIr8xU9QC_aLZYqjDXYkGiCtUg,888
|
|
10
|
+
activemodel/cli/__init__.py,sha256=HrgJjB5pRuE6hbwgy0Dw4oHvGZ47kH0LPVAdG9l6-vw,5021
|
|
11
|
+
activemodel/mixins/__init__.py,sha256=05EQl2u_Wgf_wkly-GTaTsR7zWpmpKcb96Js7r_rZTw,160
|
|
12
|
+
activemodel/mixins/pydantic_json.py,sha256=0pprGZA95BGZL4WOh--NJcvxLWey4YW85lLk4GGTjFM,3530
|
|
13
|
+
activemodel/mixins/soft_delete.py,sha256=Ax4mGsQI7AVTE8c4GiWxpyB_W179-dDct79GtjP0owU,461
|
|
14
|
+
activemodel/mixins/timestamps.py,sha256=C6QQNnzrNUOW1EAsMpEVpImEeTIYDMPP0wocEw2RDQw,1078
|
|
15
|
+
activemodel/mixins/typeid.py,sha256=777btWRUW6YBGPApeaEdHQaoKmwblehukHzmkKoXv6o,1340
|
|
16
|
+
activemodel/pytest/__init__.py,sha256=IJpD-BwJuPii5IxTJoOCryaq4_oyXNRj4RjlS5Plmc8,112
|
|
17
|
+
activemodel/pytest/factories.py,sha256=d4Mt9Lto8Pqd2bXuxDSt92IeEzTM5VGL3rQwG8jY4x4,3475
|
|
18
|
+
activemodel/pytest/plugin.py,sha256=QsHnaKmkFZjR_pUHLaU7tb47vrfcNU0zsvfBaN0g2f0,2509
|
|
19
|
+
activemodel/pytest/transaction.py,sha256=KMQ7jHSU9Bf14rPwdVAFxxR9mZ41uRAXwiSQ3HAzkw0,5801
|
|
20
|
+
activemodel/pytest/truncate.py,sha256=LeuG2fSq92oR-S1_gE-1Y3DhO48jw4ubMIRh2tb6U08,4780
|
|
21
|
+
activemodel/types/__init__.py,sha256=y5fiGVtPJxGEhuf-TvyrkhM2yaKRcIWo6XAx-CFFjM8,31
|
|
22
|
+
activemodel/types/sqlalchemy_protocol.py,sha256=2MSuGIp6pcIyiy8uK7qX3FLWABBMQOJGlIC969WRQdY,277
|
|
23
|
+
activemodel/types/sqlalchemy_protocol.pyi,sha256=SP4Z50SGcw6qSexGgNd_4g6E_sQwpIE44vgNT4ncmeI,5667
|
|
24
|
+
activemodel/types/typeid.py,sha256=qycqklKv5nKuCqjJRnxA-6MjtcWJ4vFUsAVBc1ySwfg,7865
|
|
25
|
+
activemodel/types/typeid_patch.py,sha256=y6kiCJQ_NzeKfuI4UtRAs7QW_nEog5RIA_-k4HUBMkU,575
|
|
26
|
+
activemodel-0.12.0.dist-info/METADATA,sha256=j9sLMrYMP-2pnGE5yFFYELUqgbO-H8uKNCFrpHgw-8Q,10724
|
|
27
|
+
activemodel-0.12.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
28
|
+
activemodel-0.12.0.dist-info/entry_points.txt,sha256=rytVrsNgUT4oDiW9RvRH6JBTHQn0hPZLK-jzQt3dY9s,51
|
|
29
|
+
activemodel-0.12.0.dist-info/licenses/LICENSE,sha256=L8mmpX47rB-xtJ_HsK0zpfO6viEjxbLYGn70BMp8os4,1071
|
|
30
|
+
activemodel-0.12.0.dist-info/RECORD,,
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
activemodel/__init__.py,sha256=q_lHQyIM70ApvjduTo9GtenQjJXsfYZsAAquD_51kF4,137
|
|
2
|
-
activemodel/base_model.py,sha256=VSItKKMxP-g-en_v16VnR9W6ueSLlWqmxn7I2-TgpGk,16646
|
|
3
|
-
activemodel/celery.py,sha256=L1vKcO_HoPA5ZCfsXjxgPpDUMYDuoQMakGA9rppN7Lo,897
|
|
4
|
-
activemodel/errors.py,sha256=wycWYmk9ws4TZpxvTdtXVy2SFESb8NqKgzdivBoF0vw,115
|
|
5
|
-
activemodel/get_column_from_field_patch.py,sha256=wAEDm_ZvSqyJwfgkXVpxsevw11hd-7VLy7zuJG8Ak7Y,4986
|
|
6
|
-
activemodel/logger.py,sha256=vU7QiGSy_AJuJFmClUocqIJ-Ltku_8C24ZU8L6fLJR0,53
|
|
7
|
-
activemodel/query_wrapper.py,sha256=rNdvueppMse2MIi-RafTEC34GPGRal_wqH2CzhmlWS8,2520
|
|
8
|
-
activemodel/session_manager.py,sha256=ltmUyBsYCNNddoilLWrh3HX9QY9eQSZiRsyFf0awevs,4835
|
|
9
|
-
activemodel/utils.py,sha256=g17UqkphzTmb6YdpmYwT1TM00eDiXXuWn39-xNiu0AA,2112
|
|
10
|
-
activemodel/mixins/__init__.py,sha256=05EQl2u_Wgf_wkly-GTaTsR7zWpmpKcb96Js7r_rZTw,160
|
|
11
|
-
activemodel/mixins/pydantic_json.py,sha256=0pprGZA95BGZL4WOh--NJcvxLWey4YW85lLk4GGTjFM,3530
|
|
12
|
-
activemodel/mixins/soft_delete.py,sha256=Ax4mGsQI7AVTE8c4GiWxpyB_W179-dDct79GtjP0owU,461
|
|
13
|
-
activemodel/mixins/timestamps.py,sha256=Q-IFljeVVJQqw3XHdOi7dkqzefiVg1zhJvq_bldpmjg,992
|
|
14
|
-
activemodel/mixins/typeid.py,sha256=WBZwnryF2QkI1ki0fW-jEbE8cIqMIldwkaeJdGT01S4,841
|
|
15
|
-
activemodel/pytest/__init__.py,sha256=W9KKQHbPkyq0jrMXaiL8hG2Nsbjy_LN9HhvgGm8W_7g,98
|
|
16
|
-
activemodel/pytest/transaction.py,sha256=ln-3N5tXHT0fqy6a8m_NIYg5AXAeA2hDuftQtFxNqi4,2600
|
|
17
|
-
activemodel/pytest/truncate.py,sha256=IGiPLkUm2yyOKww6c6CKcVbwi2xAAFBopx9q2ABfu8w,1582
|
|
18
|
-
activemodel/types/__init__.py,sha256=y5fiGVtPJxGEhuf-TvyrkhM2yaKRcIWo6XAx-CFFjM8,31
|
|
19
|
-
activemodel/types/typeid.py,sha256=1xB79DGIC5-P-PcLpeZW9Ed_WjFOmmVW1yl2Q3pPJis,7250
|
|
20
|
-
activemodel-0.10.0.dist-info/METADATA,sha256=rin3Edbj6CFW8-cj6fcL6S6GU8a1Qn0Sl9tGOLM-_rw,9652
|
|
21
|
-
activemodel-0.10.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
22
|
-
activemodel-0.10.0.dist-info/entry_points.txt,sha256=YLX62TP_hR-n3HMBkdBex4W7XRiyOtIPkwy22puIjjQ,61
|
|
23
|
-
activemodel-0.10.0.dist-info/licenses/LICENSE,sha256=L8mmpX47rB-xtJ_HsK0zpfO6viEjxbLYGn70BMp8os4,1071
|
|
24
|
-
activemodel-0.10.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|