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.
@@ -1,15 +1,15 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: activemodel
3
- Version: 0.10.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 activemodel import BaseModel
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,,
@@ -0,0 +1,2 @@
1
+ [pytest11]
2
+ activemodel = activemodel.pytest.plugin
@@ -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,,
@@ -1,2 +0,0 @@
1
- [console_scripts]
2
- activemodel = python_package_template:main