statikk 0.1.5__tar.gz → 0.1.7__tar.gz
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.
- {statikk-0.1.5 → statikk-0.1.7}/PKG-INFO +1 -1
- {statikk-0.1.5 → statikk-0.1.7}/src/statikk/engine.py +5 -1
- {statikk-0.1.5 → statikk-0.1.7}/src/statikk/models.py +23 -3
- {statikk-0.1.5 → statikk-0.1.7}/src/statikk.egg-info/PKG-INFO +1 -1
- {statikk-0.1.5 → statikk-0.1.7}/tests/test_engine.py +8 -3
- {statikk-0.1.5 → statikk-0.1.7}/tests/test_models.py +49 -0
- {statikk-0.1.5 → statikk-0.1.7}/.coveragerc +0 -0
- {statikk-0.1.5 → statikk-0.1.7}/.gitignore +0 -0
- {statikk-0.1.5 → statikk-0.1.7}/.readthedocs.yml +0 -0
- {statikk-0.1.5 → statikk-0.1.7}/AUTHORS.rst +0 -0
- {statikk-0.1.5 → statikk-0.1.7}/CHANGELOG.rst +0 -0
- {statikk-0.1.5 → statikk-0.1.7}/CONTRIBUTING.rst +0 -0
- {statikk-0.1.5 → statikk-0.1.7}/LICENSE.txt +0 -0
- {statikk-0.1.5 → statikk-0.1.7}/README.rst +0 -0
- {statikk-0.1.5 → statikk-0.1.7}/assets/favicon.png +0 -0
- {statikk-0.1.5 → statikk-0.1.7}/assets/logo.png +0 -0
- {statikk-0.1.5 → statikk-0.1.7}/docs/Makefile +0 -0
- {statikk-0.1.5 → statikk-0.1.7}/docs/_static/.gitignore +0 -0
- {statikk-0.1.5 → statikk-0.1.7}/docs/authors.rst +0 -0
- {statikk-0.1.5 → statikk-0.1.7}/docs/changelog.rst +0 -0
- {statikk-0.1.5 → statikk-0.1.7}/docs/conf.py +0 -0
- {statikk-0.1.5 → statikk-0.1.7}/docs/contributing.rst +0 -0
- {statikk-0.1.5 → statikk-0.1.7}/docs/index.rst +0 -0
- {statikk-0.1.5 → statikk-0.1.7}/docs/license.rst +0 -0
- {statikk-0.1.5 → statikk-0.1.7}/docs/readme.rst +0 -0
- {statikk-0.1.5 → statikk-0.1.7}/docs/requirements.txt +0 -0
- {statikk-0.1.5 → statikk-0.1.7}/docs/usage.rst +0 -0
- {statikk-0.1.5 → statikk-0.1.7}/pyproject.toml +0 -0
- {statikk-0.1.5 → statikk-0.1.7}/setup.cfg +0 -0
- {statikk-0.1.5 → statikk-0.1.7}/setup.py +0 -0
- {statikk-0.1.5 → statikk-0.1.7}/src/statikk/__init__.py +0 -0
- {statikk-0.1.5 → statikk-0.1.7}/src/statikk/conditions.py +0 -0
- {statikk-0.1.5 → statikk-0.1.7}/src/statikk/expressions.py +0 -0
- {statikk-0.1.5 → statikk-0.1.7}/src/statikk/fields.py +0 -0
- {statikk-0.1.5 → statikk-0.1.7}/src/statikk/typing.py +0 -0
- {statikk-0.1.5 → statikk-0.1.7}/src/statikk.egg-info/SOURCES.txt +0 -0
- {statikk-0.1.5 → statikk-0.1.7}/src/statikk.egg-info/dependency_links.txt +0 -0
- {statikk-0.1.5 → statikk-0.1.7}/src/statikk.egg-info/not-zip-safe +0 -0
- {statikk-0.1.5 → statikk-0.1.7}/src/statikk.egg-info/requires.txt +0 -0
- {statikk-0.1.5 → statikk-0.1.7}/src/statikk.egg-info/top_level.txt +0 -0
- {statikk-0.1.5 → statikk-0.1.7}/tests/conftest.py +0 -0
- {statikk-0.1.5 → statikk-0.1.7}/tests/test_expressions.py +0 -0
- {statikk-0.1.5 → statikk-0.1.7}/tox.ini +0 -0
@@ -232,9 +232,13 @@ class Table:
|
|
232
232
|
|
233
233
|
Returns the enriched database model instance.
|
234
234
|
"""
|
235
|
+
|
235
236
|
with self.batch_write() as batch:
|
236
237
|
for item in model.split_to_simple_objects():
|
237
|
-
|
238
|
+
if item._should_delete:
|
239
|
+
batch.delete(item)
|
240
|
+
else:
|
241
|
+
batch.put(item)
|
238
242
|
|
239
243
|
def update_item(
|
240
244
|
self,
|
@@ -67,16 +67,18 @@ class TrackingMixin:
|
|
67
67
|
|
68
68
|
def _recursive_hash(self) -> int:
|
69
69
|
"""
|
70
|
-
Compute a hash value for the model, ignoring nested DatabaseModel instances.
|
70
|
+
Compute a hash value for the model, ignoring specified fields and nested DatabaseModel instances.
|
71
71
|
|
72
|
-
This ensures that changes to child models don't affect the parent's hash.
|
72
|
+
This ensures that changes to ignored fields or child models don't affect the parent's hash.
|
73
73
|
|
74
74
|
Returns:
|
75
|
-
A hash value representing the model's non-
|
75
|
+
A hash value representing the model's non-ignored fields.
|
76
76
|
"""
|
77
77
|
if not self.should_track:
|
78
78
|
return 0
|
79
79
|
|
80
|
+
ignored_fields = self.ignore_tracking_fields()
|
81
|
+
|
80
82
|
values = []
|
81
83
|
for field_name in self.model_fields:
|
82
84
|
if not hasattr(self, field_name):
|
@@ -85,6 +87,9 @@ class TrackingMixin:
|
|
85
87
|
if field_name.startswith("_"):
|
86
88
|
continue
|
87
89
|
|
90
|
+
if field_name in ignored_fields:
|
91
|
+
continue
|
92
|
+
|
88
93
|
value = getattr(self, field_name)
|
89
94
|
|
90
95
|
if hasattr(value, "__class__") and issubclass(value.__class__, DatabaseModel):
|
@@ -141,11 +146,23 @@ class TrackingMixin:
|
|
141
146
|
|
142
147
|
return True
|
143
148
|
|
149
|
+
def ignore_tracking_fields(self) -> set:
|
150
|
+
"""
|
151
|
+
Override this method to specify fields to ignore when tracking changes.
|
152
|
+
|
153
|
+
Returns:
|
154
|
+
A set specifying fields to ignore.
|
155
|
+
Example:
|
156
|
+
{"field1", "field2"}
|
157
|
+
"""
|
158
|
+
return {}
|
159
|
+
|
144
160
|
|
145
161
|
class DatabaseModel(BaseModel, TrackingMixin, extra=Extra.allow):
|
146
162
|
id: str = Field(default_factory=lambda: str(uuid4()))
|
147
163
|
_parent: Optional[DatabaseModel] = None
|
148
164
|
_model_types_in_hierarchy: dict[str, Type[DatabaseModel]] = {}
|
165
|
+
_should_delete: bool = False
|
149
166
|
|
150
167
|
@classmethod
|
151
168
|
def type(cls) -> str:
|
@@ -240,6 +257,9 @@ class DatabaseModel(BaseModel, TrackingMixin, extra=Extra.allow):
|
|
240
257
|
return self._parent.should_write_to_database()
|
241
258
|
return True
|
242
259
|
|
260
|
+
def mark_for_delete(self):
|
261
|
+
self._should_delete = True
|
262
|
+
|
243
263
|
@classmethod
|
244
264
|
def scan(
|
245
265
|
cls,
|
@@ -788,7 +788,7 @@ def test_nested_hierarchies():
|
|
788
788
|
|
789
789
|
class DoublyNestedModel(DatabaseModel):
|
790
790
|
bar: str
|
791
|
-
items: list[TriplyNested]
|
791
|
+
items: list[TriplyNested] = []
|
792
792
|
|
793
793
|
@classmethod
|
794
794
|
def index_definitions(cls) -> dict[str, IndexFieldConfig]:
|
@@ -803,7 +803,7 @@ def test_nested_hierarchies():
|
|
803
803
|
|
804
804
|
class NestedModel(DatabaseModel):
|
805
805
|
foo: str
|
806
|
-
doubly_nested: list[DoublyNestedModel]
|
806
|
+
doubly_nested: list[DoublyNestedModel] = []
|
807
807
|
|
808
808
|
@classmethod
|
809
809
|
def index_definitions(cls) -> dict[str, IndexFieldConfig]:
|
@@ -836,7 +836,8 @@ def test_nested_hierarchies():
|
|
836
836
|
models=[ModelHierarchy, NestedModel, DoublyNestedModel, TriplyNested],
|
837
837
|
)
|
838
838
|
_create_dynamodb_table(table)
|
839
|
-
|
839
|
+
triple_nested_model = TriplyNested(faz="faz")
|
840
|
+
doubly_nested = DoublyNestedModel(bar="bar", items=[triple_nested_model])
|
840
841
|
double_nested_no_write = DoublyNestedModel(bar="far", items=[TriplyNested(faz="faz")])
|
841
842
|
nested = NestedModel(foo="foo", doubly_nested=[doubly_nested, double_nested_no_write])
|
842
843
|
model_hierarchy = ModelHierarchy(foo_id="foo_id", state="state", nested=nested)
|
@@ -850,5 +851,9 @@ def test_nested_hierarchies():
|
|
850
851
|
assert hierarchy.nested.doubly_nested[0].gsi_pk == "foo_id"
|
851
852
|
assert hierarchy.nested.doubly_nested[0].gsi_sk == "ModelHierarchy|state|NestedModel|foo|DoublyNestedModel|bar"
|
852
853
|
assert hierarchy.nested.doubly_nested[0].items[0].gsi_pk == "foo_id"
|
854
|
+
hierarchy.nested.doubly_nested[0].items[0].mark_for_delete()
|
855
|
+
hierarchy.save()
|
856
|
+
hierarchy = ModelHierarchy.query_hierarchy(hash_key=Equals("foo_id"))
|
857
|
+
assert len(hierarchy.nested.doubly_nested[0].items) == 0
|
853
858
|
hierarchy.delete()
|
854
859
|
assert list(table.scan()) == []
|
@@ -106,3 +106,52 @@ def test_tracking_disabled():
|
|
106
106
|
assert my_database_model.was_modified is True # untracked models should always be marked as modified
|
107
107
|
my_database_model.foo = "bar"
|
108
108
|
assert my_database_model.was_modified is True
|
109
|
+
|
110
|
+
|
111
|
+
def test_testing_configuration():
|
112
|
+
class MyOtherNestedDatabaseModel(DatabaseModel):
|
113
|
+
baz: str = "baz"
|
114
|
+
qux: str = "qux"
|
115
|
+
xax: str = "xax"
|
116
|
+
|
117
|
+
@classmethod
|
118
|
+
def is_nested(cls) -> bool:
|
119
|
+
return True
|
120
|
+
|
121
|
+
def ignore_tracking_fields(self) -> set:
|
122
|
+
return {"baz", "qux"}
|
123
|
+
|
124
|
+
class MyNestedDatabaseModel(DatabaseModel):
|
125
|
+
bar: str = "bar"
|
126
|
+
other_nested: MyOtherNestedDatabaseModel
|
127
|
+
|
128
|
+
@classmethod
|
129
|
+
def is_nested(cls) -> bool:
|
130
|
+
return True
|
131
|
+
|
132
|
+
class MyDatabaseModel(DatabaseModel):
|
133
|
+
foo: str = "foo"
|
134
|
+
nested: MyNestedDatabaseModel
|
135
|
+
|
136
|
+
@property
|
137
|
+
def should_track(self) -> bool:
|
138
|
+
return True
|
139
|
+
|
140
|
+
def ignore_tracking_fields(self) -> set:
|
141
|
+
return {"foo"}
|
142
|
+
|
143
|
+
my_database_model = MyDatabaseModel(
|
144
|
+
foo="foo",
|
145
|
+
nested=MyNestedDatabaseModel(
|
146
|
+
bar="bar", other_nested=MyOtherNestedDatabaseModel(baz="baz", qux="qux", xax="xax")
|
147
|
+
),
|
148
|
+
)
|
149
|
+
assert my_database_model.was_modified is False
|
150
|
+
my_database_model.foo = "bar"
|
151
|
+
assert my_database_model.was_modified is False
|
152
|
+
my_database_model.nested.other_nested.baz = "qux"
|
153
|
+
assert my_database_model.nested.other_nested.was_modified is False
|
154
|
+
my_database_model.nested.other_nested.qux = "xax"
|
155
|
+
assert my_database_model.nested.other_nested.was_modified is False
|
156
|
+
my_database_model.nested.bar = "baz"
|
157
|
+
assert my_database_model.nested.was_modified is True
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|