ormlambda 3.7.1__py3-none-any.whl → 3.11.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.
- ormlambda/__init__.py +2 -0
- ormlambda/caster/base_caster.py +3 -3
- ormlambda/common/global_checker.py +1 -1
- ormlambda/components/select/ISelect.py +4 -4
- ormlambda/components/select/__init__.py +1 -1
- ormlambda/databases/my_sql/caster/caster.py +1 -0
- ormlambda/databases/my_sql/caster/types/bytes.py +3 -3
- ormlambda/databases/my_sql/caster/types/datetime.py +3 -3
- ormlambda/databases/my_sql/caster/types/float.py +3 -3
- ormlambda/databases/my_sql/caster/types/int.py +3 -3
- ormlambda/databases/my_sql/caster/types/iterable.py +3 -3
- ormlambda/databases/my_sql/caster/types/none.py +3 -3
- ormlambda/databases/my_sql/caster/types/string.py +3 -3
- ormlambda/databases/my_sql/clauses/__init__.py +1 -0
- ormlambda/databases/my_sql/clauses/alias.py +15 -21
- ormlambda/databases/my_sql/clauses/group_by.py +19 -20
- ormlambda/databases/my_sql/clauses/having.py +16 -0
- ormlambda/databases/my_sql/clauses/order.py +6 -1
- ormlambda/databases/my_sql/clauses/update.py +1 -1
- ormlambda/databases/my_sql/clauses/where.py +3 -3
- ormlambda/databases/my_sql/functions/concat.py +8 -6
- ormlambda/databases/my_sql/join_context.py +3 -3
- ormlambda/databases/my_sql/repository/repository.py +60 -13
- ormlambda/databases/my_sql/statements.py +73 -22
- ormlambda/databases/my_sql/types.py +73 -0
- ormlambda/engine/__init__.py +2 -0
- ormlambda/engine/create.py +35 -0
- ormlambda/engine/url.py +744 -0
- ormlambda/engine/utils.py +17 -0
- ormlambda/repository/base_repository.py +2 -3
- ormlambda/repository/interfaces/IRepositoryBase.py +1 -0
- ormlambda/sql/column.py +27 -2
- ormlambda/sql/foreign_key.py +36 -4
- ormlambda/sql/table/table_constructor.py +2 -2
- ormlambda/statements/interfaces/IStatements.py +37 -25
- ormlambda/statements/types.py +4 -1
- {ormlambda-3.7.1.dist-info → ormlambda-3.11.0.dist-info}/METADATA +107 -8
- {ormlambda-3.7.1.dist-info → ormlambda-3.11.0.dist-info}/RECORD +40 -35
- {ormlambda-3.7.1.dist-info → ormlambda-3.11.0.dist-info}/LICENSE +0 -0
- {ormlambda-3.7.1.dist-info → ormlambda-3.11.0.dist-info}/WHEEL +0 -0
@@ -0,0 +1,17 @@
|
|
1
|
+
from typing import Optional, Any, TypeGuard, Iterable
|
2
|
+
import collections.abc as collections_abc
|
3
|
+
|
4
|
+
|
5
|
+
def is_non_string_iterable(obj: Any) -> TypeGuard[Iterable[Any]]:
|
6
|
+
return isinstance(obj, collections_abc.Iterable) and not isinstance(obj, (str, bytes))
|
7
|
+
|
8
|
+
|
9
|
+
def to_list(x: Any, default: Optional[list[Any]] = None) -> list[Any]:
|
10
|
+
if x is None:
|
11
|
+
return default # type: ignore
|
12
|
+
if not is_non_string_iterable(x):
|
13
|
+
return [x]
|
14
|
+
elif isinstance(x, list):
|
15
|
+
return x
|
16
|
+
else:
|
17
|
+
return list(x)
|
@@ -1,12 +1,11 @@
|
|
1
1
|
import contextlib
|
2
|
-
from typing import Generator, Type
|
2
|
+
from typing import Generator, Type, Unpack
|
3
3
|
from ormlambda.repository import IRepositoryBase
|
4
4
|
import abc
|
5
5
|
|
6
6
|
|
7
7
|
class BaseRepository[TPool](IRepositoryBase):
|
8
|
-
def __init__(self, pool: Type[TPool], **kwargs:
|
9
|
-
self._data_config: dict[str, str] = kwargs
|
8
|
+
def __init__[TArgs](self, pool: Type[TPool], **kwargs: Unpack[TArgs]) -> None:
|
10
9
|
self._pool: TPool = pool(**kwargs)
|
11
10
|
|
12
11
|
@contextlib.contextmanager
|
ormlambda/sql/column.py
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
from __future__ import annotations
|
2
|
-
from typing import Iterable, Type, Optional, TYPE_CHECKING
|
2
|
+
from typing import Iterable, Type, Optional, TYPE_CHECKING, overload
|
3
3
|
import abc
|
4
4
|
from ormlambda.sql.types import TableType, ComparerType, ColumnType
|
5
5
|
from ormlambda import ConditionType
|
@@ -25,18 +25,41 @@ class Column[TProp]:
|
|
25
25
|
"_check",
|
26
26
|
)
|
27
27
|
|
28
|
+
@overload
|
29
|
+
def __init__(self, *, column_name: str): ...
|
30
|
+
|
31
|
+
@overload
|
28
32
|
def __init__[T: Table](
|
29
33
|
self,
|
30
34
|
dtype: Type[TProp],
|
35
|
+
*,
|
31
36
|
is_primary_key: bool = False,
|
32
37
|
is_auto_generated: bool = False,
|
33
38
|
is_auto_increment: bool = False,
|
34
39
|
is_unique: bool = False,
|
35
40
|
check_types: bool = True,
|
41
|
+
) -> None: ...
|
42
|
+
|
43
|
+
def __init__[T: Table](
|
44
|
+
self,
|
45
|
+
dtype: Optional[Type[TProp]] = None,
|
46
|
+
*,
|
47
|
+
is_primary_key: bool = False,
|
48
|
+
is_auto_generated: bool = False,
|
49
|
+
is_auto_increment: bool = False,
|
50
|
+
is_unique: bool = False,
|
51
|
+
check_types: bool = True,
|
52
|
+
column_name: Optional[str] = None,
|
36
53
|
) -> None:
|
54
|
+
if dtype is None and column_name is None:
|
55
|
+
raise AttributeError("You must specify either the 'dtype' or 'column_name' attribute.")
|
56
|
+
|
57
|
+
if column_name is not None:
|
58
|
+
dtype = type(column_name)
|
59
|
+
|
37
60
|
self.dtype: Type[TProp] = dtype
|
38
61
|
self.table: Optional[TableType[T]] = None
|
39
|
-
self.column_name: Optional[str] =
|
62
|
+
self.column_name: Optional[str] = column_name
|
40
63
|
self.__private_name: Optional[str] = None
|
41
64
|
self._check = check_types
|
42
65
|
|
@@ -70,7 +93,9 @@ class Column[TProp]:
|
|
70
93
|
def __hash__(self) -> int:
|
71
94
|
return hash(
|
72
95
|
(
|
96
|
+
self.dtype,
|
73
97
|
self.column_name,
|
98
|
+
self.table,
|
74
99
|
self.is_primary_key,
|
75
100
|
self.is_auto_generated,
|
76
101
|
self.is_auto_increment,
|
ormlambda/sql/foreign_key.py
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
from __future__ import annotations
|
2
|
-
from typing import Callable, TYPE_CHECKING, Optional, Any, Type, overload
|
2
|
+
from typing import Callable, TYPE_CHECKING, Optional, Any, Type, cast, overload
|
3
3
|
|
4
4
|
from ormlambda.common.interfaces.IQueryCommand import IQuery
|
5
5
|
|
@@ -10,15 +10,45 @@ if TYPE_CHECKING:
|
|
10
10
|
from ormlambda.sql.clause_info.clause_info_context import ClauseContextType
|
11
11
|
|
12
12
|
|
13
|
+
class ForeignKeyContext(set["ForeignKey"]):
|
14
|
+
def clear(self):
|
15
|
+
to_remove = {x for x in self if not cast(ForeignKey, x)._keep_alive}
|
16
|
+
for el in to_remove:
|
17
|
+
self.remove(el)
|
18
|
+
|
19
|
+
def remove(self, element):
|
20
|
+
return super().remove(element)
|
21
|
+
|
22
|
+
def pop(self, item):
|
23
|
+
for el in self:
|
24
|
+
if el != item:
|
25
|
+
continue
|
26
|
+
|
27
|
+
if not cast(ForeignKey, el)._keep_alive:
|
28
|
+
super().remove(el)
|
29
|
+
return el
|
30
|
+
|
31
|
+
def add(self, element):
|
32
|
+
return super().add(element)
|
33
|
+
|
34
|
+
|
13
35
|
class ForeignKey[TLeft: Table, TRight: Table](IQuery):
|
14
|
-
stored_calls:
|
36
|
+
stored_calls: ForeignKeyContext = ForeignKeyContext()
|
15
37
|
|
16
38
|
@overload
|
17
39
|
def __new__[LProp, RProp](self, comparer: Comparer[LProp, RProp], clause_name: str) -> None: ...
|
18
40
|
@overload
|
19
|
-
def __new__[LProp, TRight, RProp](cls, tright: Type[TRight], relationship: Callable[[TLeft, TRight], Any | Comparer[TLeft, LProp, TRight, RProp]]) -> TRight: ...
|
41
|
+
def __new__[LProp, TRight, RProp](cls, tright: Type[TRight], relationship: Callable[[TLeft, TRight], Any | Comparer[TLeft, LProp, TRight, RProp]], keep_alive: bool = ...) -> TRight: ...
|
20
42
|
|
21
|
-
def __new__[LProp, TRight, RProp](
|
43
|
+
def __new__[LProp, TRight, RProp](
|
44
|
+
cls,
|
45
|
+
tright: Optional[TRight] = None,
|
46
|
+
relationship: Optional[Callable[[TLeft, TRight], Any | Comparer[TLeft, LProp, TRight, RProp]]] = None,
|
47
|
+
*,
|
48
|
+
comparer: Optional[Comparer] = None,
|
49
|
+
clause_name: Optional[str] = None,
|
50
|
+
keep_alive: bool = False,
|
51
|
+
) -> TRight:
|
22
52
|
return super().__new__(cls)
|
23
53
|
|
24
54
|
def __init__[LProp, RProp](
|
@@ -28,7 +58,9 @@ class ForeignKey[TLeft: Table, TRight: Table](IQuery):
|
|
28
58
|
*,
|
29
59
|
comparer: Optional[Comparer] = None,
|
30
60
|
clause_name: Optional[str] = None,
|
61
|
+
keep_alive: bool = False,
|
31
62
|
) -> None:
|
63
|
+
self._keep_alive = keep_alive
|
32
64
|
if comparer is not None and clause_name is not None:
|
33
65
|
self.__init__with_comparer(comparer, clause_name)
|
34
66
|
else:
|
@@ -170,8 +170,8 @@ class Table(metaclass=TableMeta):
|
|
170
170
|
return tuple([x for x in cls.__annotations__.values() if isinstance(x, Column)])
|
171
171
|
|
172
172
|
@classmethod
|
173
|
-
def get_column[TProp](cls,name:str) -> Column[TProp]:
|
174
|
-
for key,value in cls.__annotations__.items():
|
173
|
+
def get_column[TProp](cls, name: str) -> Column[TProp]:
|
174
|
+
for key, value in cls.__annotations__.items():
|
175
175
|
if name == key:
|
176
176
|
return value
|
177
177
|
|
@@ -29,6 +29,8 @@ from ..types import (
|
|
29
29
|
WhereTypes,
|
30
30
|
)
|
31
31
|
|
32
|
+
type SelectCols[T, TProp] = Callable[[T], ColumnType[TProp]] | ColumnType[TProp]
|
33
|
+
|
32
34
|
|
33
35
|
class IStatements[T: Table](ABC):
|
34
36
|
@abstractmethod
|
@@ -132,7 +134,7 @@ class IStatements[T: Table](ABC):
|
|
132
134
|
@abstractmethod
|
133
135
|
def count(
|
134
136
|
self,
|
135
|
-
selection: Callable[[T], tuple] =
|
137
|
+
selection: Callable[[T], tuple] = ...,
|
136
138
|
alias_clause="count",
|
137
139
|
execute: bool = False,
|
138
140
|
) -> Optional[IStatements[T]]: ...
|
@@ -160,6 +162,16 @@ class IStatements[T: Table](ABC):
|
|
160
162
|
@abstractmethod
|
161
163
|
def where[LProp, RTable, RProp](self, conditions: WhereTypes[T, LProp, RTable, RProp] = None) -> IStatements[T]: ...
|
162
164
|
|
165
|
+
# endregion
|
166
|
+
|
167
|
+
# region having
|
168
|
+
|
169
|
+
@overload
|
170
|
+
def having[LProp, RTable, RProp](self, conditions: Callable[[T], WhereTypes[T, LProp, RTable, RProp]]) -> IStatements[T]: ...
|
171
|
+
|
172
|
+
@abstractmethod
|
173
|
+
def having[LProp, RTable, RProp](self, conditions: WhereTypes[T, LProp, RTable, RProp] = None) -> IStatements[T]: ...
|
174
|
+
|
163
175
|
# endregion
|
164
176
|
# region order
|
165
177
|
@overload
|
@@ -172,14 +184,14 @@ class IStatements[T: Table](ABC):
|
|
172
184
|
# endregion
|
173
185
|
# region concat
|
174
186
|
@overload
|
175
|
-
def concat
|
187
|
+
def concat(self, selector: Callable[[T], str], alias: str = "CONCAT") -> IAggregate: ...
|
176
188
|
|
177
189
|
# endregion
|
178
190
|
# region max
|
179
191
|
@overload
|
180
192
|
def max[TProp](
|
181
193
|
self,
|
182
|
-
column:
|
194
|
+
column: SelectCols[T, TProp],
|
183
195
|
alias: Optional[str] = ...,
|
184
196
|
execute: bool = ...,
|
185
197
|
) -> TProp: ...
|
@@ -188,7 +200,7 @@ class IStatements[T: Table](ABC):
|
|
188
200
|
@overload
|
189
201
|
def min[TProp](
|
190
202
|
self,
|
191
|
-
column:
|
203
|
+
column: SelectCols[T, TProp],
|
192
204
|
alias: Optional[str] = ...,
|
193
205
|
execute: bool = ...,
|
194
206
|
) -> TProp: ...
|
@@ -197,7 +209,7 @@ class IStatements[T: Table](ABC):
|
|
197
209
|
@overload
|
198
210
|
def sum[TProp](
|
199
211
|
self,
|
200
|
-
column:
|
212
|
+
column: SelectCols[T, TProp],
|
201
213
|
alias: Optional[str] = ...,
|
202
214
|
execute: bool = ...,
|
203
215
|
) -> TProp: ...
|
@@ -208,6 +220,8 @@ class IStatements[T: Table](ABC):
|
|
208
220
|
# endregion
|
209
221
|
# region select
|
210
222
|
type SelectorType[TOri, *T] = Callable[[TOri], tuple[*T]] | tuple[*T]
|
223
|
+
type SelectorFlavourType[T, TResponse] = Optional[Callable[[T], TResponse]] | TResponse
|
224
|
+
type SelectorOneType[T, TResponse] = Callable[[T, TResponse]] | TResponse
|
211
225
|
|
212
226
|
@overload
|
213
227
|
def select[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10](self, selector: SelectorType[T, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], *, by: Optional[Enum] = ...) -> Select10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]: ...
|
@@ -234,8 +248,6 @@ class IStatements[T: Table](ABC):
|
|
234
248
|
@overload
|
235
249
|
def select(self) -> Tuple[T]: ...
|
236
250
|
|
237
|
-
type SelectorFlavourType[T, TResponse] = Optional[Callable[[T], TResponse]]
|
238
|
-
|
239
251
|
# @overload
|
240
252
|
# def select[TFlavour](self, selector: Optional[Callable[[T], tuple]] = ..., *, cast_to_tuple: bool = ..., flavour: Type[TFlavour], by: Optional[Enum] = ..., **kwargs) -> TFlavour: ...
|
241
253
|
@overload
|
@@ -243,7 +255,7 @@ class IStatements[T: Table](ABC):
|
|
243
255
|
@overload
|
244
256
|
def select[*TRes](self, selector: SelectorFlavourType[T, tuple[*TRes]] = ..., *, flavour: Type[tuple], by: Optional[Enum] = ..., **kwargs) -> tuple[tuple[*TRes]]: ...
|
245
257
|
@overload
|
246
|
-
def select[TFlavour](self, selector: SelectorFlavourType[T, tuple] = ..., *, flavour: Type[TFlavour], by: Optional[Enum] = ..., **kwargs) -> tuple[TFlavour]: ...
|
258
|
+
def select[TFlavour](self, selector: SelectorFlavourType[T, tuple] = ..., *, flavour: Type[TFlavour], by: Optional[Enum] = ..., **kwargs) -> tuple[TFlavour, ...]: ...
|
247
259
|
|
248
260
|
@abstractmethod
|
249
261
|
def select[TValue, TFlavour, P](self, selector: SelectorFlavourType[T, tuple[TValue, P]] = ..., *, cast_to_tuple: bool = ..., flavour: Type[TFlavour] = ..., by: JoinType = ..., **kwargs): ...
|
@@ -255,21 +267,21 @@ class IStatements[T: Table](ABC):
|
|
255
267
|
@overload
|
256
268
|
def select_one[TFlavour](self, *, by: Optional[Enum] = ..., flavour: Type[TFlavour], **kwargs) -> TFlavour: ...
|
257
269
|
@overload
|
258
|
-
def select_one[T1](self, selector:
|
270
|
+
def select_one[T1](self, selector: SelectorOneType[T, T1 | tuple[T1]], *, by: Optional[Enum] = ...) -> T1: ...
|
259
271
|
@overload
|
260
|
-
def select_one[*TRes](self, selector:
|
272
|
+
def select_one[*TRes](self, selector: SelectorOneType[T, tuple[*TRes]], *, by: Optional[Enum] = ...) -> tuple[*TRes]: ...
|
261
273
|
@overload
|
262
|
-
def select_one[T1](self, selector:
|
274
|
+
def select_one[T1](self, selector: SelectorOneType[T, tuple[T1]], *, by: Optional[Enum] = ..., flavour: Type, **kwargs) -> T1: ...
|
263
275
|
@overload
|
264
|
-
def select_one[T1, TFlavour](self, selector:
|
276
|
+
def select_one[T1, TFlavour](self, selector: SelectorOneType[T, T1], *, by: Optional[Enum] = ..., flavour: Type[TFlavour], **kwargs) -> TFlavour: ...
|
265
277
|
@overload
|
266
|
-
def select_one[*TRest](self, selector:
|
278
|
+
def select_one[*TRest](self, selector: SelectorOneType[T, tuple[*TRest]], *, by: Optional[Enum] = ..., flavour: Type[tuple], **kwargs) -> tuple[*TRest]: ...
|
267
279
|
@overload
|
268
|
-
def select_one[TFlavour](self, selector:
|
280
|
+
def select_one[TFlavour](self, selector: SelectorOneType[T, tuple], *, by: Optional[Enum] = ..., flavour: Type[TFlavour], **kwargs) -> TFlavour: ...
|
269
281
|
@abstractmethod
|
270
282
|
def select_one[TValue, TFlavour, *TRest](
|
271
283
|
self,
|
272
|
-
selector: Optional[
|
284
|
+
selector: Optional[SelectorOneType[T, tuple[TValue, *TRest]]] = lambda: None,
|
273
285
|
*,
|
274
286
|
flavour: Type[TFlavour] = ...,
|
275
287
|
by: Optional[Enum] = ...,
|
@@ -281,23 +293,23 @@ class IStatements[T: Table](ABC):
|
|
281
293
|
@overload
|
282
294
|
def first(self) -> T: ...
|
283
295
|
@overload
|
284
|
-
def first[
|
296
|
+
def first[T1](self, selector: SelectorOneType[T, T1 | tuple[T1]], *, by: Optional[Enum] = ...) -> T1: ...
|
285
297
|
@overload
|
286
|
-
def first[
|
298
|
+
def first[*TRes](self, selector: SelectorOneType[T, tuple[*TRes]], *, by: Optional[Enum] = ...) -> tuple[*TRes]: ...
|
287
299
|
@overload
|
288
|
-
def first[
|
300
|
+
def first[TFlavour](self, *, by: Optional[Enum] = ..., flavour: Type[TFlavour], **kwargs) -> TFlavour: ...
|
289
301
|
@overload
|
290
|
-
def first[T1](self, selector:
|
302
|
+
def first[T1](self, selector: SelectorOneType[T, tuple[T1]], *, by: Optional[Enum] = ..., flavour: Type, **kwargs) -> T1: ...
|
291
303
|
@overload
|
292
|
-
def first[T1, TFlavour](self, selector:
|
304
|
+
def first[T1, TFlavour](self, selector: SelectorOneType[T, T1], *, by: Optional[Enum] = ..., flavour: Type[TFlavour], **kwargs) -> TFlavour: ...
|
293
305
|
@overload
|
294
|
-
def first[*TRest](self, selector:
|
306
|
+
def first[*TRest](self, selector: SelectorOneType[T, tuple[*TRest]], *, by: Optional[Enum] = ..., flavour: Type[tuple], **kwargs) -> tuple[*TRest]: ...
|
295
307
|
@overload
|
296
|
-
def first[TFlavour](self, selector:
|
308
|
+
def first[TFlavour](self, selector: SelectorOneType[T, tuple], *, by: Optional[Enum] = ..., flavour: Type[TFlavour], **kwargs) -> TFlavour: ...
|
297
309
|
@abstractmethod
|
298
310
|
def first[TValue, TFlavour, *TRest](
|
299
311
|
self,
|
300
|
-
selector: Optional[
|
312
|
+
selector: Optional[SelectorOneType[T, tuple[TValue, *TRest]]] = lambda: None,
|
301
313
|
*,
|
302
314
|
flavour: Type[TFlavour] = ...,
|
303
315
|
by: Optional[Enum] = ...,
|
@@ -305,9 +317,9 @@ class IStatements[T: Table](ABC):
|
|
305
317
|
|
306
318
|
# endregion
|
307
319
|
|
308
|
-
# region
|
320
|
+
# region groupby
|
309
321
|
@abstractmethod
|
310
|
-
def
|
322
|
+
def groupby[TRepo](self, column: Callable[[T], TRepo]) -> IStatements[T]: ...
|
311
323
|
|
312
324
|
# endregion
|
313
325
|
|
ormlambda/statements/types.py
CHANGED
@@ -18,7 +18,10 @@ if TYPE_CHECKING:
|
|
18
18
|
type OrderTypes = Literal["ASC", "DESC"] | OrderType | Iterable[OrderType]
|
19
19
|
|
20
20
|
|
21
|
-
class OrderType(enum.Enum):
|
21
|
+
class OrderType(str, enum.Enum):
|
22
|
+
def __str__(self):
|
23
|
+
return super().__str__()
|
24
|
+
|
22
25
|
ASC = "ASC"
|
23
26
|
DESC = "DESC"
|
24
27
|
|
@@ -1,15 +1,13 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: ormlambda
|
3
|
-
Version: 3.
|
3
|
+
Version: 3.11.0
|
4
4
|
Summary: ORM designed to interact with the database (currently with MySQL) using lambda functions and nested functions
|
5
5
|
Author: p-hzamora
|
6
6
|
Author-email: p.hzamora@icloud.com
|
7
7
|
Requires-Python: >=3.12,<4.0
|
8
8
|
Classifier: Programming Language :: Python :: 3
|
9
9
|
Classifier: Programming Language :: Python :: 3.12
|
10
|
-
Requires-Dist: fluent-validation (==4.3.1)
|
11
10
|
Requires-Dist: mysql-connector-python (>=9.0.0,<10.0.0)
|
12
|
-
Requires-Dist: python-dotenv (>=1.0.1,<2.0.0)
|
13
11
|
Requires-Dist: shapely (>=2.0.6,<3.0.0)
|
14
12
|
Description-Content-Type: text/markdown
|
15
13
|
|
@@ -17,7 +15,7 @@ Description-Content-Type: text/markdown
|
|
17
15
|

|
18
16
|

|
19
17
|
|
20
|
-
#
|
18
|
+
# ormlambda Documentation
|
21
19
|
This ORM is designed to connect with a MySQL server, facilitating the management of various database queries. Built with flexibility and efficiency in mind, this ORM empowers developers to interact with the database using lambda functions, allowing for concise and expressive query construction.
|
22
20
|
|
23
21
|
# Creating your first lambda query
|
@@ -38,19 +36,34 @@ database = MySQLRepository(user=USERNAME, password=PASSWORD, database="sakila",
|
|
38
36
|
## Select all columns
|
39
37
|
```python
|
40
38
|
from ormlambda import ORM
|
41
|
-
from ormlambda
|
39
|
+
from ormlambda import create_engine
|
42
40
|
|
43
41
|
from models.address import Address
|
44
|
-
from config import config_dict
|
42
|
+
from test.config import config_dict
|
45
43
|
|
46
|
-
db =
|
44
|
+
db = create_engine('mysql://root:1234@localhost:3306/sakila')
|
47
45
|
|
48
|
-
AddressModel = ORM(Address,db)
|
46
|
+
AddressModel = ORM(Address, db)
|
49
47
|
|
50
48
|
result = AddressModel.select()
|
51
49
|
```
|
52
50
|
The `result` var will be of type `tuple[Address, ...]`
|
53
51
|
|
52
|
+
## Improving Typing
|
53
|
+
For those cases where you need to pass the database configuration from a `dict`, you can use `MySQLArgs` TypedDict object to improve type annotations.
|
54
|
+
|
55
|
+
```python
|
56
|
+
from ormlambda.databases.my_sql.types import MySQLArgs
|
57
|
+
|
58
|
+
config_dict: MySQLArgs = {
|
59
|
+
"user": DB_USERNAME,
|
60
|
+
"password": DB_PASSWORD,
|
61
|
+
"host": DB_HOST,
|
62
|
+
"database": DB_DATABASE,
|
63
|
+
}
|
64
|
+
db = MySQLRepository(**config_dict)
|
65
|
+
```
|
66
|
+
|
54
67
|
## Select multiples tables
|
55
68
|
Once the `AddressModel` class is created, we will not only be able to access all the information in that table, but also all the information in all the tables that have foreign keys related to it."
|
56
69
|
|
@@ -296,6 +309,92 @@ res = AddressModel.sum(Address.address_id, execute=True)
|
|
296
309
|
res = AddressModel.count(Address.address_id, execute=True)
|
297
310
|
```
|
298
311
|
|
312
|
+
## 1. Concat
|
313
|
+
|
314
|
+
The `concat` method allows you to concatenate multiple columns or values into a single string. This is particularly useful for creating derived fields in your queries.
|
315
|
+
|
316
|
+
#### Usage
|
317
|
+
|
318
|
+
```python
|
319
|
+
response = ORM(Address, db).where(Address.City.Country.country.regex(r"^Spain")).first(
|
320
|
+
(
|
321
|
+
Address.address,
|
322
|
+
Address.City.city,
|
323
|
+
self.tmodel.concat(
|
324
|
+
(
|
325
|
+
"Address: ",
|
326
|
+
Address.address,
|
327
|
+
" - city: ",
|
328
|
+
Address.City.city,
|
329
|
+
" - country: ",
|
330
|
+
Address.City.Country.country,
|
331
|
+
)
|
332
|
+
),
|
333
|
+
),
|
334
|
+
flavour=dict,
|
335
|
+
)
|
336
|
+
|
337
|
+
{
|
338
|
+
"address_address": "939 Probolinggo Loop",
|
339
|
+
"city_city": "A Coruña (La Coruña)",
|
340
|
+
"CONCAT": "Address: 939 Probolinggo Loop - city: A Coruña (La Coruña) - country: Spain",
|
341
|
+
}
|
342
|
+
```
|
343
|
+
As you can see in the response, the result is a dictionary where the keys are a combination of the table name and the column name. This is done to avoid collisions with columns from other tables that might have the same name.
|
344
|
+
|
345
|
+
Another elegant approach to adjust the response and obtain an object is by using the `flavour` attribute. You can pass a callable object, which will be used to instantiate it with the returned data.
|
346
|
+
|
347
|
+
## Using BaseModel for Custom Responses (Pydantic)
|
348
|
+
|
349
|
+
You can utilize `BaseModel` from Pydantic to create structured response models. This allows you to define the expected structure of your data, ensuring type safety and validation.
|
350
|
+
|
351
|
+
### Example: Creating a Custom Response Model
|
352
|
+
|
353
|
+
You can create a custom response model by subclassing `BaseModel`. In this model, you define the fields that you expect in your response, along with their types.
|
354
|
+
|
355
|
+
```python
|
356
|
+
class AddressCombine(BaseModel):
|
357
|
+
address: str
|
358
|
+
city: str
|
359
|
+
country: str
|
360
|
+
|
361
|
+
model_config: ConfigDict = {"extra": "forbid"}
|
362
|
+
|
363
|
+
ddbb = MySQLRepository(**config_dict)
|
364
|
+
model = ORM(Address, ddbb)
|
365
|
+
select = (
|
366
|
+
model.order(lambda x: x.City.Country.country, "DESC")
|
367
|
+
.limit(10)
|
368
|
+
.where(Address.City.Country.country == "Spain")
|
369
|
+
.first(
|
370
|
+
lambda x: (
|
371
|
+
x.address,
|
372
|
+
x.City.city,
|
373
|
+
x.City.Country.country,
|
374
|
+
),
|
375
|
+
flavour=AddressCombine,
|
376
|
+
)
|
377
|
+
)
|
378
|
+
```
|
379
|
+
|
380
|
+
Once you execute the query, the result will be an instance of your custom model. You can access the fields directly, ensuring that the data adheres to the structure you defined.
|
381
|
+
|
382
|
+
```python
|
383
|
+
|
384
|
+
|
385
|
+
print(select.address)
|
386
|
+
print(select.city)
|
387
|
+
print(select.country)
|
388
|
+
```
|
389
|
+
|
390
|
+
|
391
|
+
<!-- ### 2. Having
|
392
|
+
|
393
|
+
The `having` method is used to filter results based on aggregate functions. It is typically used in conjunction with `group by` clauses.
|
394
|
+
|
395
|
+
#### Usage -->
|
396
|
+
|
397
|
+
|
299
398
|
## Combine aggregation method
|
300
399
|
As shown in the previous examples, setting the `execute` attribute to `True` allows us to perform the corresponding query in a single line. However, if you're looking to improve efficiency, you can combine all of them into one query.
|
301
400
|
```python
|