alpha-python 0.1.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.
- alpha/__init__.py +0 -0
- alpha/adapters/__init__.py +0 -0
- alpha/adapters/sqla_unit_of_work.py +120 -0
- alpha/domain/__init__.py +0 -0
- alpha/domain/models/__init__.py +0 -0
- alpha/domain/models/base_model.py +25 -0
- alpha/encoder.py +62 -0
- alpha/exceptions.py +99 -0
- alpha/factories/__init__.py +0 -0
- alpha/factories/_type_conversion_matrix.py +233 -0
- alpha/factories/_type_mapping.py +29 -0
- alpha/factories/class_factories.py +496 -0
- alpha/factories/default_field_factory.py +50 -0
- alpha/factories/field_iterator.py +188 -0
- alpha/factories/logging_handler_factory.py +86 -0
- alpha/factories/model_class_factory.py +176 -0
- alpha/factories/models/__init__.py +0 -0
- alpha/factories/models/factory_classes.py +20 -0
- alpha/factories/request_factory.py +211 -0
- alpha/factories/response_factory.py +186 -0
- alpha/factories/type_factories.py +204 -0
- alpha/infra/__init__.py +0 -0
- alpha/infra/database/__init__.py +0 -0
- alpha/infra/database/sql_alchemy_database.py +159 -0
- alpha/infra/database/sql_alchemy_view.py +48 -0
- alpha/infra/models/__init__.py +0 -0
- alpha/infra/models/filter_operators.py +98 -0
- alpha/infra/models/json_patch.py +21 -0
- alpha/infra/models/order_by.py +69 -0
- alpha/infra/models/query_clause.py +45 -0
- alpha/infra/models/search_filter.py +586 -0
- alpha/interfaces/__init__.py +0 -0
- alpha/interfaces/attrs_instance.py +10 -0
- alpha/interfaces/dataclass_instance.py +11 -0
- alpha/interfaces/factories.py +102 -0
- alpha/interfaces/openapi_model.py +21 -0
- alpha/interfaces/patchable.py +8 -0
- alpha/interfaces/sql_database.py +36 -0
- alpha/interfaces/sql_mapper.py +23 -0
- alpha/interfaces/sql_repository.py +380 -0
- alpha/interfaces/token_factory.py +56 -0
- alpha/interfaces/unit_of_work.py +53 -0
- alpha/interfaces/updateable.py +7 -0
- alpha/py.typed +0 -0
- alpha/repositories/__init__.py +0 -0
- alpha/repositories/default_sql_repository.py +679 -0
- alpha/repositories/models/__init__.py +0 -0
- alpha/repositories/models/repository_model.py +16 -0
- alpha/services/__init__.py +0 -0
- alpha/services/authentication_service.py +71 -0
- alpha/utils/__init__.py +0 -0
- alpha/utils/_http_codes.py +148 -0
- alpha/utils/is_attrs.py +18 -0
- alpha/utils/logging_configurator.py +133 -0
- alpha/utils/logging_level_checker.py +26 -0
- alpha/utils/response_object.py +26 -0
- alpha/utils/version_check.py +17 -0
- alpha_python-0.1.0.dist-info/METADATA +22 -0
- alpha_python-0.1.0.dist-info/RECORD +62 -0
- alpha_python-0.1.0.dist-info/WHEEL +5 -0
- alpha_python-0.1.0.dist-info/licenses/LICENSE +21 -0
- alpha_python-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,679 @@
|
|
|
1
|
+
"""Contains the DefaultSqlRepository implementation which provides
|
|
2
|
+
basic CRUD operations for domain models using SqlAlchemy."""
|
|
3
|
+
|
|
4
|
+
import json
|
|
5
|
+
import logging
|
|
6
|
+
from enum import Enum
|
|
7
|
+
from typing import Any, Generic, Iterable, cast
|
|
8
|
+
from uuid import UUID
|
|
9
|
+
|
|
10
|
+
from sqlalchemy import BinaryExpression, ColumnElement, ColumnOperators
|
|
11
|
+
from sqlalchemy.exc import IntegrityError
|
|
12
|
+
from sqlalchemy.orm import (
|
|
13
|
+
Query,
|
|
14
|
+
Session,
|
|
15
|
+
)
|
|
16
|
+
from sqlalchemy.orm.attributes import InstrumentedAttribute
|
|
17
|
+
from sqlalchemy.sql.elements import UnaryExpression
|
|
18
|
+
|
|
19
|
+
from alpha import exceptions
|
|
20
|
+
from alpha.domain.models.base_model import (
|
|
21
|
+
BaseDomainModel,
|
|
22
|
+
DomainModel,
|
|
23
|
+
)
|
|
24
|
+
from alpha.encoder import JSONEncoder
|
|
25
|
+
from alpha.infra.models.order_by import OrderBy
|
|
26
|
+
from alpha.infra.models.search_filter import SearchFilter
|
|
27
|
+
from alpha.infra.models.query_clause import QueryClause
|
|
28
|
+
from alpha.infra.models.filter_operators import FilterOperator
|
|
29
|
+
from alpha.infra.models.json_patch import JsonPatch
|
|
30
|
+
from alpha.interfaces.updateable import Updateable
|
|
31
|
+
from alpha.utils.logging_level_checker import logging_level_checker as llc
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class DefaultSqlRepository(Generic[DomainModel]):
|
|
35
|
+
"""SqlAlchemy default repository implementation. Provides basic
|
|
36
|
+
CRUD operations for domain models.
|
|
37
|
+
|
|
38
|
+
The repository uses a SqlAlchemy session to interact with the database. It
|
|
39
|
+
requires a default domain model type to be specified which will be used
|
|
40
|
+
for operations where no specific model type is provided. The following
|
|
41
|
+
operations are supported:
|
|
42
|
+
- add
|
|
43
|
+
- add_all
|
|
44
|
+
- count
|
|
45
|
+
- get
|
|
46
|
+
- get_all
|
|
47
|
+
- get_one
|
|
48
|
+
- get_one_or_none
|
|
49
|
+
- get_by_id
|
|
50
|
+
- patch
|
|
51
|
+
- remove
|
|
52
|
+
- remove_all
|
|
53
|
+
- select
|
|
54
|
+
- update
|
|
55
|
+
- view
|
|
56
|
+
|
|
57
|
+
You can also extend this repository to add custom methods by inheriting
|
|
58
|
+
from it and adding your own methods.
|
|
59
|
+
|
|
60
|
+
Example:
|
|
61
|
+
```python
|
|
62
|
+
class CustomRepository(DefaultSqlRepository[MyDomainModel]):
|
|
63
|
+
def custom_method(self, param: str) -> list[MyDomainModel]:
|
|
64
|
+
# Custom query logic here
|
|
65
|
+
pass
|
|
66
|
+
```
|
|
67
|
+
"""
|
|
68
|
+
|
|
69
|
+
def __init__(self, session: Session, default_model: DomainModel) -> None:
|
|
70
|
+
"""_summary_
|
|
71
|
+
|
|
72
|
+
Parameters
|
|
73
|
+
----------
|
|
74
|
+
session : Session
|
|
75
|
+
_description_
|
|
76
|
+
default_model : DomainModel
|
|
77
|
+
_description_
|
|
78
|
+
"""
|
|
79
|
+
self.session = session
|
|
80
|
+
self._default_model = default_model
|
|
81
|
+
|
|
82
|
+
def add(
|
|
83
|
+
self,
|
|
84
|
+
obj: DomainModel,
|
|
85
|
+
return_obj: bool = True,
|
|
86
|
+
raise_if_exists: bool = False,
|
|
87
|
+
) -> DomainModel | None:
|
|
88
|
+
"""_summary_
|
|
89
|
+
|
|
90
|
+
Parameters
|
|
91
|
+
----------
|
|
92
|
+
obj : DomainModel
|
|
93
|
+
_description_
|
|
94
|
+
return_obj : bool, optional
|
|
95
|
+
_description_, by default True
|
|
96
|
+
raise_if_exists : bool, optional
|
|
97
|
+
_description_, by default False
|
|
98
|
+
|
|
99
|
+
Returns
|
|
100
|
+
-------
|
|
101
|
+
DomainModel | None
|
|
102
|
+
_description_
|
|
103
|
+
|
|
104
|
+
Raises
|
|
105
|
+
------
|
|
106
|
+
exceptions.AlreadyExistsException
|
|
107
|
+
_description_
|
|
108
|
+
"""
|
|
109
|
+
try:
|
|
110
|
+
self.session.add(obj)
|
|
111
|
+
self.session.flush()
|
|
112
|
+
if return_obj:
|
|
113
|
+
self.session.refresh(obj)
|
|
114
|
+
if llc("debug"):
|
|
115
|
+
logging.debug(
|
|
116
|
+
"added object to database session: %s",
|
|
117
|
+
json.dumps(obj, cls=JSONEncoder),
|
|
118
|
+
)
|
|
119
|
+
logging.debug("flushed pending transaction to session")
|
|
120
|
+
if return_obj:
|
|
121
|
+
if llc("debug"):
|
|
122
|
+
logging.debug(
|
|
123
|
+
"refreshed object: %s",
|
|
124
|
+
json.dumps(obj, cls=JSONEncoder),
|
|
125
|
+
)
|
|
126
|
+
return obj
|
|
127
|
+
except IntegrityError as exc:
|
|
128
|
+
self.session.rollback()
|
|
129
|
+
if llc("debug"):
|
|
130
|
+
logging.debug("rolled back pending transaction from session")
|
|
131
|
+
if raise_if_exists:
|
|
132
|
+
raise exceptions.AlreadyExistsException(exc)
|
|
133
|
+
return None
|
|
134
|
+
|
|
135
|
+
def add_all(
|
|
136
|
+
self,
|
|
137
|
+
objs: list[DomainModel],
|
|
138
|
+
return_obj: bool = False,
|
|
139
|
+
raise_if_exists: bool = False,
|
|
140
|
+
) -> list[DomainModel] | None:
|
|
141
|
+
"""_summary_
|
|
142
|
+
|
|
143
|
+
Parameters
|
|
144
|
+
----------
|
|
145
|
+
objs : list[DomainModel]
|
|
146
|
+
_description_
|
|
147
|
+
return_obj : bool, optional
|
|
148
|
+
_description_, by default False
|
|
149
|
+
raise_if_exists : bool, optional
|
|
150
|
+
_description_, by default False
|
|
151
|
+
|
|
152
|
+
Returns
|
|
153
|
+
-------
|
|
154
|
+
list[DomainModel] | None
|
|
155
|
+
_description_
|
|
156
|
+
|
|
157
|
+
Raises
|
|
158
|
+
------
|
|
159
|
+
exceptions.AlreadyExistsException
|
|
160
|
+
_description_
|
|
161
|
+
"""
|
|
162
|
+
if return_obj:
|
|
163
|
+
objects: list[DomainModel] | None = []
|
|
164
|
+
for obj in objs:
|
|
165
|
+
object_ = self.add(
|
|
166
|
+
obj=obj,
|
|
167
|
+
return_obj=return_obj,
|
|
168
|
+
raise_if_exists=raise_if_exists,
|
|
169
|
+
)
|
|
170
|
+
objects.append(object_) # type: ignore
|
|
171
|
+
return objects
|
|
172
|
+
try:
|
|
173
|
+
self.session.bulk_save_objects(objs)
|
|
174
|
+
if llc("debug"):
|
|
175
|
+
logging.debug(
|
|
176
|
+
"bulk added objects to database session: %s",
|
|
177
|
+
json.dumps(objs, cls=JSONEncoder),
|
|
178
|
+
)
|
|
179
|
+
self.session.flush()
|
|
180
|
+
if llc("debug"):
|
|
181
|
+
logging.debug("flushed pending transactions to session")
|
|
182
|
+
except IntegrityError as exc:
|
|
183
|
+
self.session.rollback()
|
|
184
|
+
if llc("debug"):
|
|
185
|
+
logging.debug("rolled back pending transaction from session")
|
|
186
|
+
if raise_if_exists:
|
|
187
|
+
raise exceptions.AlreadyExistsException(exc)
|
|
188
|
+
for obj in objs:
|
|
189
|
+
self.add(obj)
|
|
190
|
+
return None
|
|
191
|
+
|
|
192
|
+
def count(
|
|
193
|
+
self,
|
|
194
|
+
model: DomainModel | None = None,
|
|
195
|
+
**kwargs: Any,
|
|
196
|
+
) -> int:
|
|
197
|
+
"""_summary_
|
|
198
|
+
|
|
199
|
+
Parameters
|
|
200
|
+
----------
|
|
201
|
+
model : DomainModel | None, optional
|
|
202
|
+
_description_, by default None
|
|
203
|
+
|
|
204
|
+
Returns
|
|
205
|
+
-------
|
|
206
|
+
int
|
|
207
|
+
_description_
|
|
208
|
+
"""
|
|
209
|
+
return self._query(cursor_result="count", model=model, **kwargs) # type: ignore
|
|
210
|
+
|
|
211
|
+
def get(
|
|
212
|
+
self,
|
|
213
|
+
attr: str | InstrumentedAttribute[Any],
|
|
214
|
+
value: str | int | float | Enum | UUID,
|
|
215
|
+
cursor_result: str = "first",
|
|
216
|
+
model: DomainModel | None = None,
|
|
217
|
+
**kwargs: Any,
|
|
218
|
+
) -> DomainModel:
|
|
219
|
+
"""_summary_
|
|
220
|
+
|
|
221
|
+
Parameters
|
|
222
|
+
----------
|
|
223
|
+
attr : str | InstrumentedAttribute
|
|
224
|
+
_description_
|
|
225
|
+
value : str | int | float | Enum | UUID
|
|
226
|
+
_description_
|
|
227
|
+
cursor_result : str, optional
|
|
228
|
+
_description_, by default "first"
|
|
229
|
+
model : DomainModel | None, optional
|
|
230
|
+
_description_, by default None
|
|
231
|
+
|
|
232
|
+
Returns
|
|
233
|
+
-------
|
|
234
|
+
DomainModel
|
|
235
|
+
_description_
|
|
236
|
+
"""
|
|
237
|
+
|
|
238
|
+
if isinstance(attr, InstrumentedAttribute):
|
|
239
|
+
attr = attr.key
|
|
240
|
+
return self._query(
|
|
241
|
+
cursor_result=cursor_result,
|
|
242
|
+
filter_by={attr: value},
|
|
243
|
+
model=model,
|
|
244
|
+
**kwargs, # type: ignore
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
def get_all(
|
|
248
|
+
self,
|
|
249
|
+
attr: str | InstrumentedAttribute[Any],
|
|
250
|
+
value: str | int | float | Enum | UUID,
|
|
251
|
+
cursor_result: str = "all",
|
|
252
|
+
model: DomainModel | None = None,
|
|
253
|
+
**kwargs: Any,
|
|
254
|
+
) -> list[DomainModel]:
|
|
255
|
+
"""_summary_
|
|
256
|
+
|
|
257
|
+
Parameters
|
|
258
|
+
----------
|
|
259
|
+
attr : str | InstrumentedAttribute
|
|
260
|
+
_description_
|
|
261
|
+
value : str | int | float | Enum | UUID
|
|
262
|
+
_description_
|
|
263
|
+
cursor_result : str, optional
|
|
264
|
+
_description_, by default "all"
|
|
265
|
+
model : DomainModel | None, optional
|
|
266
|
+
_description_, by default None
|
|
267
|
+
|
|
268
|
+
Returns
|
|
269
|
+
-------
|
|
270
|
+
list[DomainModel]
|
|
271
|
+
_description_
|
|
272
|
+
"""
|
|
273
|
+
objs = self.get(
|
|
274
|
+
attr=attr,
|
|
275
|
+
value=value,
|
|
276
|
+
cursor_result=cursor_result,
|
|
277
|
+
model=model,
|
|
278
|
+
**kwargs,
|
|
279
|
+
)
|
|
280
|
+
return objs # type: ignore
|
|
281
|
+
|
|
282
|
+
def get_one(
|
|
283
|
+
self,
|
|
284
|
+
attr: str | InstrumentedAttribute[Any],
|
|
285
|
+
value: str | int | float | Enum | UUID,
|
|
286
|
+
cursor_result: str = "one",
|
|
287
|
+
model: DomainModel | None = None,
|
|
288
|
+
**kwargs: Any,
|
|
289
|
+
) -> DomainModel:
|
|
290
|
+
"""_summary_
|
|
291
|
+
|
|
292
|
+
Parameters
|
|
293
|
+
----------
|
|
294
|
+
attr : str | InstrumentedAttribute
|
|
295
|
+
_description_
|
|
296
|
+
value : str | int | float | Enum | UUID
|
|
297
|
+
_description_
|
|
298
|
+
cursor_result : str, optional
|
|
299
|
+
_description_, by default "one"
|
|
300
|
+
model : DomainModel | None, optional
|
|
301
|
+
_description_, by default None
|
|
302
|
+
|
|
303
|
+
Returns
|
|
304
|
+
-------
|
|
305
|
+
DomainModel
|
|
306
|
+
_description_
|
|
307
|
+
"""
|
|
308
|
+
return self.get(
|
|
309
|
+
attr=attr,
|
|
310
|
+
value=value,
|
|
311
|
+
cursor_result=cursor_result,
|
|
312
|
+
model=model,
|
|
313
|
+
**kwargs,
|
|
314
|
+
)
|
|
315
|
+
|
|
316
|
+
def get_one_or_none(
|
|
317
|
+
self,
|
|
318
|
+
attr: str | InstrumentedAttribute[Any],
|
|
319
|
+
value: str | int | float | Enum | UUID,
|
|
320
|
+
cursor_result: str = "one_or_none",
|
|
321
|
+
model: DomainModel | None = None,
|
|
322
|
+
**kwargs: Any,
|
|
323
|
+
) -> DomainModel | None:
|
|
324
|
+
"""_summary_
|
|
325
|
+
|
|
326
|
+
Parameters
|
|
327
|
+
----------
|
|
328
|
+
attr : str | InstrumentedAttribute
|
|
329
|
+
_description_
|
|
330
|
+
value : str | int | float | Enum | UUID
|
|
331
|
+
_description_
|
|
332
|
+
cursor_result : str, optional
|
|
333
|
+
_description_, by default "one_or_none"
|
|
334
|
+
model : DomainModel | None, optional
|
|
335
|
+
_description_, by default None
|
|
336
|
+
|
|
337
|
+
Returns
|
|
338
|
+
-------
|
|
339
|
+
DomainModel | None
|
|
340
|
+
_description_
|
|
341
|
+
"""
|
|
342
|
+
return self.get(
|
|
343
|
+
attr=attr,
|
|
344
|
+
value=value,
|
|
345
|
+
cursor_result=cursor_result,
|
|
346
|
+
model=model,
|
|
347
|
+
**kwargs,
|
|
348
|
+
)
|
|
349
|
+
|
|
350
|
+
def get_by_id(
|
|
351
|
+
self,
|
|
352
|
+
value: str | int | UUID,
|
|
353
|
+
attr: str | InstrumentedAttribute[Any] = "id",
|
|
354
|
+
cursor_result: str = "one_or_none",
|
|
355
|
+
model: DomainModel | None = None,
|
|
356
|
+
**kwargs: Any,
|
|
357
|
+
) -> DomainModel | None:
|
|
358
|
+
"""_summary_
|
|
359
|
+
|
|
360
|
+
Parameters
|
|
361
|
+
----------
|
|
362
|
+
value : str | int | UUID
|
|
363
|
+
_description_
|
|
364
|
+
attr : str | InstrumentedAttribute, optional
|
|
365
|
+
_description_, by default "id"
|
|
366
|
+
cursor_result : str, optional
|
|
367
|
+
_description_, by default "one_or_none"
|
|
368
|
+
model : DomainModel | None, optional
|
|
369
|
+
_description_, by default None
|
|
370
|
+
|
|
371
|
+
Returns
|
|
372
|
+
-------
|
|
373
|
+
DomainModel | None
|
|
374
|
+
_description_
|
|
375
|
+
"""
|
|
376
|
+
return self.get(
|
|
377
|
+
attr=attr,
|
|
378
|
+
value=value,
|
|
379
|
+
cursor_result=cursor_result,
|
|
380
|
+
model=model,
|
|
381
|
+
**kwargs,
|
|
382
|
+
)
|
|
383
|
+
|
|
384
|
+
def patch(
|
|
385
|
+
self, obj: BaseDomainModel, patches: JsonPatch
|
|
386
|
+
) -> BaseDomainModel:
|
|
387
|
+
"""Patch a domain model object using a JSON patch document.
|
|
388
|
+
|
|
389
|
+
Parameters
|
|
390
|
+
----------
|
|
391
|
+
obj
|
|
392
|
+
Patchable object to be patched.
|
|
393
|
+
patches
|
|
394
|
+
JSON patch document containing the changes to apply.
|
|
395
|
+
|
|
396
|
+
Returns
|
|
397
|
+
-------
|
|
398
|
+
DomainModel
|
|
399
|
+
Patched object.
|
|
400
|
+
"""
|
|
401
|
+
if not hasattr(obj, "patch"):
|
|
402
|
+
raise TypeError("Object does not support patch operation")
|
|
403
|
+
patched = obj.patch(patches) # type: ignore[attr-defined]
|
|
404
|
+
self.session.flush()
|
|
405
|
+
return cast(BaseDomainModel, patched)
|
|
406
|
+
|
|
407
|
+
def remove(self, obj: DomainModel) -> None:
|
|
408
|
+
"""_summary_
|
|
409
|
+
|
|
410
|
+
Parameters
|
|
411
|
+
----------
|
|
412
|
+
obj : DomainModel
|
|
413
|
+
_description_
|
|
414
|
+
"""
|
|
415
|
+
self.session.delete(obj)
|
|
416
|
+
self.session.flush()
|
|
417
|
+
|
|
418
|
+
def remove_all(
|
|
419
|
+
self,
|
|
420
|
+
objs: list[DomainModel] | None = None,
|
|
421
|
+
**kwargs: Any,
|
|
422
|
+
) -> None:
|
|
423
|
+
"""_summary_
|
|
424
|
+
|
|
425
|
+
Parameters
|
|
426
|
+
----------
|
|
427
|
+
objs : list[DomainModel] | None, optional
|
|
428
|
+
_description_, by default None
|
|
429
|
+
"""
|
|
430
|
+
if not objs:
|
|
431
|
+
objs = self.select(**kwargs) # type: ignore
|
|
432
|
+
for obj in objs:
|
|
433
|
+
self.remove(obj)
|
|
434
|
+
|
|
435
|
+
def select(
|
|
436
|
+
self,
|
|
437
|
+
model: DomainModel | None = None,
|
|
438
|
+
cursor_result: str = "all",
|
|
439
|
+
**kwargs: Any,
|
|
440
|
+
) -> list[DomainModel]:
|
|
441
|
+
"""_summary_
|
|
442
|
+
|
|
443
|
+
Parameters
|
|
444
|
+
----------
|
|
445
|
+
model : DomainModel | None, optional
|
|
446
|
+
_description_, by default None
|
|
447
|
+
cursor_result : str, optional
|
|
448
|
+
_description_, by default "all"
|
|
449
|
+
|
|
450
|
+
Returns
|
|
451
|
+
-------
|
|
452
|
+
list[DomainModel]
|
|
453
|
+
_description_
|
|
454
|
+
"""
|
|
455
|
+
return self._query(cursor_result=cursor_result, model=model, **kwargs) # type: ignore
|
|
456
|
+
|
|
457
|
+
def update(self, obj: Updateable, new: DomainModel) -> DomainModel:
|
|
458
|
+
"""_summary_
|
|
459
|
+
|
|
460
|
+
Parameters
|
|
461
|
+
----------
|
|
462
|
+
obj : DomainModel
|
|
463
|
+
_description_
|
|
464
|
+
new : DomainModel
|
|
465
|
+
_description_
|
|
466
|
+
|
|
467
|
+
Returns
|
|
468
|
+
-------
|
|
469
|
+
DomainModel
|
|
470
|
+
_description_
|
|
471
|
+
"""
|
|
472
|
+
obj = obj.update(new)
|
|
473
|
+
self.session.flush()
|
|
474
|
+
self.session.refresh(obj)
|
|
475
|
+
return obj
|
|
476
|
+
|
|
477
|
+
def view(
|
|
478
|
+
self,
|
|
479
|
+
model: DomainModel,
|
|
480
|
+
cursor_result: str = "all",
|
|
481
|
+
**kwargs: Any,
|
|
482
|
+
) -> list[DomainModel]:
|
|
483
|
+
"""_summary_
|
|
484
|
+
|
|
485
|
+
Parameters
|
|
486
|
+
----------
|
|
487
|
+
model : DomainModel
|
|
488
|
+
_description_
|
|
489
|
+
cursor_result : str, optional
|
|
490
|
+
_description_, by default "all"
|
|
491
|
+
|
|
492
|
+
Returns
|
|
493
|
+
-------
|
|
494
|
+
list[DomainModel]
|
|
495
|
+
_description_
|
|
496
|
+
"""
|
|
497
|
+
return self._query(cursor_result=cursor_result, model=model, **kwargs) # type: ignore
|
|
498
|
+
|
|
499
|
+
def _query(
|
|
500
|
+
self,
|
|
501
|
+
cursor_result: str | None = None,
|
|
502
|
+
model: DomainModel | None = None,
|
|
503
|
+
filters: Iterable[SearchFilter | FilterOperator] | None = None,
|
|
504
|
+
query: Query[Any] | None = None,
|
|
505
|
+
order_by: list[
|
|
506
|
+
InstrumentedAttribute[Any]
|
|
507
|
+
| UnaryExpression[Any]
|
|
508
|
+
| OrderBy
|
|
509
|
+
| QueryClause
|
|
510
|
+
] = list(),
|
|
511
|
+
**kwargs: Any,
|
|
512
|
+
) -> Any:
|
|
513
|
+
"""_summary_
|
|
514
|
+
|
|
515
|
+
Parameters
|
|
516
|
+
----------
|
|
517
|
+
cursor_result : str | None, optional
|
|
518
|
+
_description_, by default None
|
|
519
|
+
model : DomainModel | None, optional
|
|
520
|
+
_description_, by default None
|
|
521
|
+
filters : list[SearchFilter | QueryClause | FilterOperator], optional
|
|
522
|
+
_description_, by default list()
|
|
523
|
+
query : Query[Any] | None, optional
|
|
524
|
+
_description_, by default None
|
|
525
|
+
order_by : list[ InstrumentedAttribute[Any] | UnaryExpression[Any] | OrderBy | QueryClause ], optional
|
|
526
|
+
_description_, by default list()
|
|
527
|
+
|
|
528
|
+
Returns
|
|
529
|
+
-------
|
|
530
|
+
Any
|
|
531
|
+
_description_
|
|
532
|
+
"""
|
|
533
|
+
"""
|
|
534
|
+
cursor_result:
|
|
535
|
+
all
|
|
536
|
+
first
|
|
537
|
+
one
|
|
538
|
+
one_or_none
|
|
539
|
+
count
|
|
540
|
+
None
|
|
541
|
+
|
|
542
|
+
**kwargs:
|
|
543
|
+
limit=n
|
|
544
|
+
order_by=User.id
|
|
545
|
+
order_by=[User.username, User.birthday]
|
|
546
|
+
distinct=User.username
|
|
547
|
+
|
|
548
|
+
"""
|
|
549
|
+
if not model:
|
|
550
|
+
model = self._default_model
|
|
551
|
+
|
|
552
|
+
subquery: Query[Any]
|
|
553
|
+
|
|
554
|
+
if query:
|
|
555
|
+
subquery = query
|
|
556
|
+
else:
|
|
557
|
+
subquery = self.session.query(model) # type: ignore
|
|
558
|
+
|
|
559
|
+
normalized_filters = list(filters) if filters else []
|
|
560
|
+
if normalized_filters:
|
|
561
|
+
filter_statements = self._process_filters(
|
|
562
|
+
filters=normalized_filters, model=model
|
|
563
|
+
)
|
|
564
|
+
subquery = subquery.filter(*filter_statements) # type: ignore
|
|
565
|
+
|
|
566
|
+
for k, value in kwargs.items():
|
|
567
|
+
if not value:
|
|
568
|
+
break
|
|
569
|
+
|
|
570
|
+
if isinstance(value, QueryClause):
|
|
571
|
+
subquery = self._query_clause(
|
|
572
|
+
clause=value, query=subquery, model=model # type: ignore
|
|
573
|
+
)
|
|
574
|
+
elif isinstance(value, dict): # type: ignore
|
|
575
|
+
subquery = getattr(subquery, k)(**value) # type: ignore
|
|
576
|
+
elif isinstance(value, list):
|
|
577
|
+
for item in value: # type: ignore
|
|
578
|
+
if isinstance(item, QueryClause):
|
|
579
|
+
subquery = self._query_clause(
|
|
580
|
+
clause=item, query=subquery, model=model # type: ignore
|
|
581
|
+
)
|
|
582
|
+
else:
|
|
583
|
+
subquery = getattr(subquery, k)(item) # type: ignore
|
|
584
|
+
else:
|
|
585
|
+
subquery = getattr(subquery, k)(value) # type: ignore
|
|
586
|
+
|
|
587
|
+
for order in order_by:
|
|
588
|
+
if isinstance(order, QueryClause):
|
|
589
|
+
subquery = self._query_clause(
|
|
590
|
+
clause=order, query=subquery, model=model # type: ignore
|
|
591
|
+
)
|
|
592
|
+
elif isinstance(
|
|
593
|
+
order, InstrumentedAttribute | UnaryExpression
|
|
594
|
+
): # type: ignore
|
|
595
|
+
subquery = getattr(subquery, "order_by")(order) # type: ignore
|
|
596
|
+
|
|
597
|
+
# Process cursor_result parameter
|
|
598
|
+
if cursor_result:
|
|
599
|
+
return getattr(subquery, cursor_result)() # type: ignore
|
|
600
|
+
|
|
601
|
+
return subquery # type: ignore
|
|
602
|
+
|
|
603
|
+
def _query_clause(
|
|
604
|
+
self,
|
|
605
|
+
clause: QueryClause,
|
|
606
|
+
query: Query[Any],
|
|
607
|
+
model: DomainModel,
|
|
608
|
+
) -> Query[Any]:
|
|
609
|
+
if not clause._domain_model: # type: ignore
|
|
610
|
+
clause.set_domain_model(model)
|
|
611
|
+
return clause.query_clause(query)
|
|
612
|
+
|
|
613
|
+
def _process_filters(
|
|
614
|
+
self,
|
|
615
|
+
filters: Iterable[SearchFilter | FilterOperator],
|
|
616
|
+
model: BaseDomainModel,
|
|
617
|
+
) -> list[ColumnElement[Any] | BinaryExpression[Any] | ColumnOperators]:
|
|
618
|
+
"""Process query filters and apply them to the query object
|
|
619
|
+
|
|
620
|
+
Parameters
|
|
621
|
+
----------
|
|
622
|
+
filters
|
|
623
|
+
Filters which can be SearchFilter or FilerOperator objects
|
|
624
|
+
model
|
|
625
|
+
The domain model which will be used to set the `_domain_model`
|
|
626
|
+
attribute of SearchFilter objects
|
|
627
|
+
|
|
628
|
+
Returns
|
|
629
|
+
-------
|
|
630
|
+
Query object to which the filters have been applied
|
|
631
|
+
"""
|
|
632
|
+
filter_expressions = [
|
|
633
|
+
self._process_filter_item(filter_=f, model=model) for f in filters
|
|
634
|
+
]
|
|
635
|
+
return filter_expressions
|
|
636
|
+
|
|
637
|
+
def _process_filter_item(
|
|
638
|
+
self,
|
|
639
|
+
filter_: SearchFilter | FilterOperator,
|
|
640
|
+
model: BaseDomainModel,
|
|
641
|
+
) -> ColumnElement[Any] | BinaryExpression[Any] | ColumnOperators:
|
|
642
|
+
"""Process a filter item. When the item is a SeachFilter object
|
|
643
|
+
the domain model will be set and the filter statement will be returned.
|
|
644
|
+
When the item is a FilterOperator object, all the filters will be
|
|
645
|
+
processed recursively by this method and they are supplied to the
|
|
646
|
+
filter operator.
|
|
647
|
+
|
|
648
|
+
Parameters
|
|
649
|
+
----------
|
|
650
|
+
filter_
|
|
651
|
+
A filter object
|
|
652
|
+
model
|
|
653
|
+
Domain model type
|
|
654
|
+
|
|
655
|
+
Returns
|
|
656
|
+
-------
|
|
657
|
+
Returns a filter statement or a filter operator containing
|
|
658
|
+
filter statements
|
|
659
|
+
|
|
660
|
+
Raises
|
|
661
|
+
------
|
|
662
|
+
TypeError
|
|
663
|
+
When an unsupported filter type is being used
|
|
664
|
+
"""
|
|
665
|
+
if isinstance(filter_, FilterOperator):
|
|
666
|
+
filters = [
|
|
667
|
+
self._process_filter_item(filter_=filter_item, model=model)
|
|
668
|
+
for filter_item in filter_.search_filters
|
|
669
|
+
]
|
|
670
|
+
return filter_.filter_operator(*filters) # type: ignore
|
|
671
|
+
elif isinstance(filter_, SearchFilter): # type: ignore
|
|
672
|
+
if not filter_._domain_model: # type: ignore
|
|
673
|
+
filter_.set_domain_model(model) # type: ignore
|
|
674
|
+
return filter_.filter_statement
|
|
675
|
+
else:
|
|
676
|
+
raise TypeError(
|
|
677
|
+
"Only QueryClause and FilterOperator types are allowed "
|
|
678
|
+
"as values for the 'filters' argument"
|
|
679
|
+
)
|
|
File without changes
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"""_summary_"""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
|
|
5
|
+
from alpha.domain.models.base_model import BaseDomainModel
|
|
6
|
+
from alpha.interfaces.sql_repository import SqlRepository
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@dataclass
|
|
10
|
+
class RepositoryModel:
|
|
11
|
+
"""_summary_"""
|
|
12
|
+
|
|
13
|
+
name: str
|
|
14
|
+
repository: type[SqlRepository[BaseDomainModel]]
|
|
15
|
+
default_model: type[BaseDomainModel]
|
|
16
|
+
interface: object | None
|
|
File without changes
|