ormlambda 2.9.0__py3-none-any.whl → 2.9.4__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,5 +1,5 @@
1
1
  from __future__ import annotations
2
- from typing import Any, Callable, Iterable, Optional, Literal, Type, Union, overload, TYPE_CHECKING, TypeVar
2
+ from typing import Any, Callable, Iterable, Optional, Literal, Type, Union, overload, TYPE_CHECKING
3
3
  from enum import Enum
4
4
  from abc import abstractmethod, ABC
5
5
  import enum
@@ -17,21 +17,48 @@ class OrderType(enum.Enum):
17
17
  DESC = "DESC"
18
18
 
19
19
 
20
- OrderTypeString = Literal["ASC", "DESC"]
20
+ type OrderTypes = Literal["ASC", "DESC"] | OrderType | Iterable[OrderType]
21
+
22
+
23
+ type Tuple[T] = tuple[T, ...]
24
+
25
+ type SelectRes1[T1] = tuple[Tuple[T1]]
26
+ type SelectRes2[T1, T2] = tuple[*SelectRes1[T1], Tuple[T2]]
27
+ type SelectRes3[T1, T2, T3] = tuple[*SelectRes2[T1, T2], Tuple[T3]]
28
+ type SelectRes4[T1, T2, T3, T4] = tuple[*SelectRes3[T1, T2, T3], Tuple[T4]]
29
+ type SelectRes5[T1, T2, T3, T4, T5] = tuple[*SelectRes4[T1, T2, T3, T4], Tuple[T5]]
30
+ type SelectRes6[T1, T2, T3, T4, T5, T6] = tuple[*SelectRes5[T1, T2, T3, T4, T5], Tuple[T6]]
31
+ type SelectRes7[T1, T2, T3, T4, T5, T6, T7] = tuple[*SelectRes6[T1, T2, T3, T4, T5, T6], Tuple[T7]]
32
+ type SelectRes8[T1, T2, T3, T4, T5, T6, T7, T8] = tuple[*SelectRes7[T1, T2, T3, T4, T5, T6, T7], Tuple[T8]]
33
+ type SelectRes9[T1, T2, T3, T4, T5, T6, T7, T8, T9] = tuple[*SelectRes8[T1, T2, T3, T4, T5, T6, T7, T8], Tuple[T9]]
34
+ type SelectRes10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10] = tuple[*SelectRes9[T1, T2, T3, T4, T5, T6, T7, T8, T9], Tuple[T10]]
35
+
36
+
37
+ type WhereCondition[T, T1] = Callable[[T, T1], bool]
38
+ type JoinCondition[T, T1] = tuple[T1, WhereCondition[T, T1]]
39
+
40
+ type TupleJoins1[T, T1] = tuple[JoinCondition[T, T1]]
41
+ type TupleJoins2[T, T1, T2] = tuple[*TupleJoins1[T, T1], JoinCondition[T, T2]]
42
+ type TupleJoins3[T, T1, T2, T3] = tuple[*TupleJoins2[T, T1, T2], JoinCondition[T, T3]]
43
+ type TupleJoins4[T, T1, T2, T3, T4] = tuple[*TupleJoins3[T, T1, T2, T3], JoinCondition[T, T4]]
44
+ type TupleJoins5[T, T1, T2, T3, T4, T5] = tuple[*TupleJoins4[T, T1, T2, T3, T4], JoinCondition[T, T5]]
45
+ type TupleJoins6[T, T1, T2, T3, T4, T5, T6] = tuple[*TupleJoins5[T, T1, T2, T3, T4, T5], JoinCondition[T, T6]]
21
46
 
22
- OrderTypes = OrderTypeString | OrderType | Iterable[OrderType]
23
47
 
24
48
  # TODOH: This var is duplicated from 'src\ormlambda\databases\my_sql\clauses\create_database.py'
25
49
  TypeExists = Literal["fail", "replace", "append"]
26
50
 
27
- T = TypeVar("T")
28
- WhereTypes = Union[Callable[[T], bool], Iterable[Callable[[T], bool]]]
51
+ type WhereTypes[T, *Ts] = Union[Callable[[T, *Ts], bool], Iterable[Callable[[T, *Ts], bool]]]
29
52
 
30
53
 
31
- class IStatements[T: Table](ABC):
54
+ class IStatements[T, *Ts](ABC):
32
55
  @abstractmethod
33
56
  def create_table(self, if_exists: TypeExists) -> None: ...
34
57
 
58
+ # #TODOL [ ]: We must to implement this mehtod
59
+ # @abstractmethod
60
+ # def drop_table(self)->None: ...
61
+
35
62
  @abstractmethod
36
63
  def table_exists(self) -> bool: ...
37
64
 
@@ -121,15 +148,10 @@ class IStatements[T: Table](ABC):
121
148
  def delete(self, instance: Optional[T | list[T]] = ...) -> None: ...
122
149
 
123
150
  # endregion
124
- # region join
125
- @abstractmethod
126
- def join(self, table_left: Table, table_right: Table, *, by: str) -> IStatements[T]: ...
127
151
 
128
- # endregion
129
152
  # region where
130
-
131
153
  @overload
132
- def where(self, conditions: Iterable[Callable[[T], bool]]) -> IStatements[T]:
154
+ def where(self, conditions: Iterable[Callable[[T, *Ts], bool]]) -> IStatements[T, *Ts]:
133
155
  """
134
156
  This method creates where clause by passing the Iterable in lambda function
135
157
  EXAMPLE
@@ -140,7 +162,7 @@ class IStatements[T: Table](ABC):
140
162
  ...
141
163
 
142
164
  @overload
143
- def where(self, conditions: Callable[[T], bool], **kwargs) -> IStatements[T]:
165
+ def where(self, conditions: Callable[[T, *Ts], bool], **kwargs) -> IStatements[T, *Ts]:
144
166
  """
145
167
  PARAM
146
168
  -
@@ -157,7 +179,7 @@ class IStatements[T: Table](ABC):
157
179
  ...
158
180
 
159
181
  @abstractmethod
160
- def where(self, conditions: WhereTypes = lambda: None, **kwargs) -> IStatements[T]: ...
182
+ def where(self, conditions: WhereTypes[T, *Ts] = lambda: None, **kwargs) -> IStatements[T, *Ts]: ...
161
183
 
162
184
  # endregion
163
185
  # region order
@@ -200,44 +222,68 @@ class IStatements[T: Table](ABC):
200
222
  alias: bool = ...,
201
223
  alias_name: Optional[str] = ...,
202
224
  ) -> TProp: ...
225
+
226
+ # endregion
227
+ # region join
228
+
229
+ @overload
230
+ def join[T1](self, joins: JoinCondition[T, T1]) -> IStatements[T, T1]: ...
231
+ @overload
232
+ def join[T1](self, joins: TupleJoins1[T, T1]) -> IStatements[T, T1]: ...
233
+ @overload
234
+ def join[T1, T2](self, joins: TupleJoins2[T, T1, T2]) -> IStatements[T, T1, T2]: ...
235
+ @overload
236
+ def join[T1, T2, T3](self, joins: TupleJoins3[T, T1, T2, T3]) -> IStatements[T, T1, T2, T3]: ...
237
+ @overload
238
+ def join[T1, T2, T3, T4](self, joins: TupleJoins4[T, T1, T2, T3, T4]) -> IStatements[T, T1, T2, T3, T4]: ...
239
+ @overload
240
+ def join[T1, T2, T3, T4, T5](self, joins: TupleJoins5[T, T1, T2, T3, T4, T5]) -> IStatements[T, T1, T2, T3, T4, T5]: ...
241
+ @overload
242
+ def join[T1, T2, T3, T4, T5, T6](self, joins: TupleJoins6[T, T1, T2, T3, T4, T5, T6]) -> IStatements[T, T1, T2, T3, T4, T5, T6]: ...
243
+
244
+ @abstractmethod
245
+ def join[*FKTables](self, joins) -> IStatements[T, *FKTables]: ...
246
+
203
247
  # endregion
204
248
  # region select
205
249
  @overload
206
- def select(self) -> tuple[T, ...]: ...
250
+ def select(self) -> SelectRes1[T]: ...
207
251
  @overload
208
- def select[T1](self, selector: Callable[[T], T1], *, by: Optional[Enum] = ...) -> tuple[tuple[T1, ...]]: ...
252
+ def select[T1](self, selector: Callable[[T], T1], *, by: Optional[Enum] = ...) -> SelectRes1[T1]: ...
209
253
  @overload
210
- def select[T1](self, selector: Callable[[T], tuple[T1]], *, by: Optional[Enum] = ...) -> tuple[tuple[T1, ...]]: ...
254
+ def select[T1](self, selector: Callable[[T], tuple[T1]], *, by: Optional[Enum] = ...) -> SelectRes1[T1]: ...
211
255
  @overload
212
- def select[T1, T2](self, selector: Callable[[T], tuple[T1, T2]], *, by: Optional[Enum] = ...) -> tuple[tuple[T1, ...], tuple[T2, ...]]: ...
256
+ def select[T1, T2](self, selector: Callable[[T, *Ts], tuple[T1, T2]], *, by: Optional[Enum] = ...) -> SelectRes2[T1, T2]: ...
213
257
  @overload
214
- def select[T1, T2, T3](self, selector: Callable[[T], tuple[T1, T2, T3]], *, by: Optional[Enum] = ...) -> tuple[tuple[T1, ...], tuple[T2, ...], tuple[T3, ...]]: ...
258
+ def select[T1, T2, T3](self, selector: Callable[[T, *Ts], tuple[T1, T2, T3]], *, by: Optional[Enum] = ...) -> SelectRes3[T1, T2, T3]: ...
215
259
  @overload
216
- def select[T1, T2, T3, T4](self, selector: Callable[[T], tuple[T1, T2, T3, T4]], *, by: Optional[Enum] = ...) -> tuple[tuple[T1, ...], tuple[T2, ...], tuple[T3, ...], tuple[T4, ...]]: ...
260
+ def select[T1, T2, T3, T4](self, selector: Callable[[T, *Ts], tuple[T1, T2, T3, T4]], *, by: Optional[Enum] = ...) -> SelectRes4[T1, T2, T3, T4]: ...
217
261
  @overload
218
- def select[T1, T2, T3, T4, T5](self, selector: Callable[[T], tuple[T1, T2, T3, T4, T5]], *, by: Optional[Enum] = ...) -> tuple[tuple[T1, ...], tuple[T2, ...], tuple[T3, ...], tuple[T4, ...], tuple[T5, ...]]: ...
262
+ def select[T1, T2, T3, T4, T5](self, selector: Callable[[T, *Ts], tuple[T1, T2, T3, T4, T5]], *, by: Optional[Enum] = ...) -> SelectRes5[T1, T2, T3, T4, T5]: ...
219
263
  @overload
220
- def select[T1, T2, T3, T4, T5, T6](self, selector: Callable[[T], tuple[T1, T2, T3, T4, T5, T6]], *, by: Optional[Enum] = ...) -> tuple[tuple[T1, ...], tuple[T2, ...], tuple[T3, ...], tuple[T4, ...], tuple[T5, ...], tuple[T6, ...]]: ...
264
+ def select[T1, T2, T3, T4, T5, T6](self, selector: Callable[[T, *Ts], tuple[T1, T2, T3, T4, T5, T6]], *, by: Optional[Enum] = ...) -> SelectRes6[T1, T2, T3, T4, T5, T6]: ...
221
265
  @overload
222
- def select[T1, T2, T3, T4, T5, T6, T7](self, selector: Callable[[T], tuple[T1, T2, T3, T4, T5, T6, T7]], *, by: Optional[Enum] = ...) -> tuple[tuple[T1, ...], tuple[T2, ...], tuple[T3, ...], tuple[T4, ...], tuple[T5, ...], tuple[T6, ...], tuple[T7, ...]]: ...
266
+ def select[T1, T2, T3, T4, T5, T6, T7](self, selector: Callable[[T, *Ts], tuple[T1, T2, T3, T4, T5, T6, T7]], *, by: Optional[Enum] = ...) -> SelectRes7[T1, T2, T3, T4, T5, T6, T7]: ...
223
267
  @overload
224
- def select[T1, T2, T3, T4, T5, T6, T7, T8](self, selector: Callable[[T], tuple[T1, T2, T3, T4, T5, T6, T7, T8]], *, by: Optional[Enum] = ...) -> tuple[tuple[T1, ...], tuple[T2, ...], tuple[T3, ...], tuple[T4, ...], tuple[T5, ...], tuple[T6, ...], tuple[T7, ...], tuple[T8, ...]]: ...
268
+ def select[T1, T2, T3, T4, T5, T6, T7, T8](self, selector: Callable[[T, *Ts], tuple[T1, T2, T3, T4, T5, T6, T7, T8]], *, by: Optional[Enum] = ...) -> SelectRes8[T1, T2, T3, T4, T5, T6, T7, T8]: ...
225
269
  @overload
226
- def select[T1, T2, T3, T4, T5, T6, T7, T8, T9](self, selector: Callable[[T], tuple[T1, T2, T3, T4, T5, T6, T7, T8, T9]], *, by: Optional[Enum] = ...) -> tuple[tuple[T1, ...], tuple[T2, ...], tuple[T3, ...], tuple[T4, ...], tuple[T5, ...], tuple[T6, ...], tuple[T7, ...], tuple[T8, ...], tuple[T9, ...]]: ...
270
+ def select[T1, T2, T3, T4, T5, T6, T7, T8, T9](self, selector: Callable[[T, *Ts], tuple[T1, T2, T3, T4, T5, T6, T7, T8, T9]], *, by: Optional[Enum] = ...) -> SelectRes9[T1, T2, T3, T4, T5, T6, T7, T8, T9]: ...
227
271
  @overload
228
- def select[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10](
229
- self, selector: Callable[[T], tuple[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], *, by: Optional[Enum] = ...
230
- ) -> tuple[tuple[T1, ...], tuple[T2, ...], tuple[T3, ...], tuple[T4, ...], tuple[T5, ...], tuple[T6, ...], tuple[T7, ...], tuple[T8, ...], tuple[T9, ...], tuple[T10, ...]]: ...
272
+ def select[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10](self, selector: Callable[[T, *Ts], tuple[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], *, by: Optional[Enum] = ...) -> SelectRes10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]: ...
273
+
231
274
  @overload
232
- def select[Ts](self, selector: Optional[Callable[[T], Ts]] = ..., *, flavour: Type[tuple], by: Optional[Enum] = ...) -> tuple[Ts, ...]: ...
275
+ def select[TFlavour](self, selector: Optional[Callable[[T, *Ts], tuple]] = ..., *, cast_to_tuple: bool = ..., flavour: Type[TFlavour], by: Optional[Enum] = ..., **kwargs) -> TFlavour: ...
233
276
  @overload
234
- def select[Ts](self, selector: Optional[Callable[[T], tuple[Ts]]] = ..., *, flavour: Type[tuple], by: Optional[Enum] = ...) -> tuple[Ts, ...]: ...
277
+ def select[TRes](self, selector: Optional[Callable[[T, *Ts], TRes]] = ..., *, flavour: Type[tuple], by: Optional[Enum] = ..., **kwargs) -> tuple[TRes, ...]: ...
235
278
  @overload
236
- def select[*Ts](self, selector: Optional[Callable[[T], tuple[*Ts]]] = ..., *, flavour: Type[tuple], by: Optional[Enum] = ...) -> tuple[tuple[*Ts]]: ...
279
+ def select[TRes](self, selector: Optional[Callable[[T, *Ts], tuple[TRes]]] = ..., *, flavour: Type[tuple], by: Optional[Enum] = ..., **kwargs) -> tuple[TRes, ...]: ...
237
280
  @overload
238
- def select[TFlavour](self, selector: Optional[Callable[[T], tuple]] = ..., *, flavour: Type[TFlavour], by: Optional[Enum] = ...) -> tuple[TFlavour]: ...
281
+ def select[*TRes](self, selector: Optional[Callable[[T, *Ts], tuple[*TRes]]] = ..., *, flavour: Type[tuple], by: Optional[Enum] = ..., **kwargs) -> tuple[tuple[*TRes]]: ...
282
+ @overload
283
+ def select[TFlavour](self, selector: Optional[Callable[[T, *Ts], tuple]] = ..., *, flavour: Type[TFlavour], by: Optional[Enum] = ..., **kwargs) -> tuple[TFlavour]: ...
284
+
239
285
  @abstractmethod
240
- def select[TValue, TFlavour, *Ts](self, selector: Optional[Callable[[T], tuple[TValue, *Ts]]] = ..., *, flavour: Type[TFlavour] = ..., by: JoinType = ...): ...
286
+ def select[TValue, TFlavour, *Ts](self, selector: Optional[Callable[[T, *Ts], tuple[TValue, *Ts]]] = ..., *, cast_to_tuple: bool = ..., flavour: Type[TFlavour] = ..., by: JoinType = ..., **kwargs): ...
241
287
 
242
288
  # endregion
243
289
  # region select_one
@@ -246,24 +292,24 @@ class IStatements[T: Table](ABC):
246
292
  @overload
247
293
  def select_one[TFlavour](self, *, by: Optional[Enum] = ..., flavour: Type[TFlavour]) -> TFlavour: ...
248
294
  @overload
249
- def select_one[T1](self, selector: Callable[[T], tuple[T1]], *, by: Optional[Enum] = ...) -> T1: ...
295
+ def select_one[T1](self, selector: Callable[[T, *Ts], tuple[T1]], *, by: Optional[Enum] = ...) -> T1: ...
250
296
  @overload
251
- def select_one[*Ts](self, selector: Callable[[T], tuple[*Ts]], *, by: Optional[Enum] = ...) -> tuple[*Ts]: ...
297
+ def select_one[*TRes](self, selector: Callable[[T, *Ts], tuple[*TRes]], *, by: Optional[Enum] = ...) -> tuple[*TRes]: ...
252
298
  @overload
253
- def select_one[T1](self, selector: Callable[[T], tuple[T1]], *, by: Optional[Enum] = ..., flavour: Type) -> T1: ...
299
+ def select_one[T1](self, selector: Callable[[T, *Ts], tuple[T1]], *, by: Optional[Enum] = ..., flavour: Type) -> T1: ...
254
300
  @overload
255
- def select_one[T1, TFlavour](self, selector: Callable[[T], T1], *, by: Optional[Enum] = ..., flavour: Type[TFlavour]) -> T1: ...
301
+ def select_one[T1, TFlavour](self, selector: Callable[[T, *Ts], T1], *, by: Optional[Enum] = ..., flavour: Type[TFlavour]) -> T1: ...
256
302
  @overload
257
- def select_one[*Ts](self, selector: Callable[[T], tuple[*Ts]], *, by: Optional[Enum] = ..., flavour: Type[tuple]) -> tuple[*Ts]: ...
303
+ def select_one[*Ts](self, selector: Callable[[T, *Ts], tuple[*Ts]], *, by: Optional[Enum] = ..., flavour: Type[tuple]) -> tuple[*Ts]: ...
258
304
  @overload
259
- def select_one[TFlavour](self, selector: Callable[[T], tuple], *, by: Optional[Enum] = ..., flavour: Type[TFlavour]) -> TFlavour: ...
305
+ def select_one[TFlavour](self, selector: Callable[[T, *Ts], tuple], *, by: Optional[Enum] = ..., flavour: Type[TFlavour]) -> TFlavour: ...
260
306
  @abstractmethod
261
- def select_one[TValue, TFlavour, *Ts](self, selector: Optional[Callable[[T], tuple[TValue, *Ts]]] = lambda: None, *, flavour: Type[TFlavour] = ..., by: Optional[Enum] = ...): ...
307
+ def select_one[TValue, TFlavour, *Ts](self, selector: Optional[Callable[[T, *Ts], tuple[TValue, *Ts]]] = lambda: None, *, flavour: Type[TFlavour] = ..., by: Optional[Enum] = ...): ...
262
308
 
263
309
  # endregion
264
310
  # region group_by
265
311
  @abstractmethod
266
- def group_by[TRepo, *Ts](self, column: Callable[[T], TRepo]) -> IStatements[T]: ...
312
+ def group_by[TRepo](self, column: Callable[[T, *Ts], TRepo]) -> IStatements[T, *Ts]: ...
267
313
 
268
314
  # endregion
269
315
 
@@ -271,7 +317,16 @@ class IStatements[T: Table](ABC):
271
317
  def _build(self) -> str: ...
272
318
 
273
319
 
274
- class IStatements_two_generic[T: Table, TRepo](IStatements[T]):
320
+ class IStatements_two_generic[T: Table, *Ts, TRepo](IStatements[T, *Ts]):
275
321
  @property
276
322
  @abstractmethod
277
323
  def repository(self) -> IRepositoryBase[TRepo]: ...
324
+
325
+ @property
326
+ def query(self) -> str: ...
327
+
328
+ @property
329
+ def model(self) -> Type[T]: ...
330
+
331
+ @property
332
+ def models(self) -> tuple[*Ts]: ...
@@ -0,0 +1,31 @@
1
+ import typing as tp
2
+ from ormlambda.common.abstract_classes.decomposition_query import ClauseInfo, DecompositionQueryBase
3
+ from ormlambda import Table
4
+
5
+ from ormlambda.common.interfaces import ICustomAlias
6
+
7
+
8
+ class Alias[T: tp.Type[Table], *Ts](DecompositionQueryBase[T,*Ts], ICustomAlias[T,*Ts]):
9
+ def __init__(
10
+ self,
11
+ table: T,
12
+ query: tp.Callable[[T, *Ts], tp.Any],
13
+ *,
14
+ alias: bool = True,
15
+ alias_name: str | None = None,
16
+ ) -> None:
17
+ super().__init__(
18
+ table,
19
+ lambda_query=query,
20
+ alias=alias,
21
+ alias_name=alias_name,
22
+ )
23
+
24
+ def alias_children_resolver[Tclause: tp.Type[Table]](self, clause_info: ClauseInfo[Tclause]):
25
+ return self.alias_name
26
+
27
+ @property
28
+ def query(self) -> str:
29
+ assert len(self.all_clauses) == 1
30
+
31
+ return self.all_clauses[0].query
@@ -5,13 +5,13 @@ from ormlambda.common.interfaces.IAggregate import IAggregate
5
5
  from ormlambda import Table
6
6
 
7
7
 
8
- class GroupBy[T: tp.Type[Table], TProp](DecompositionQueryBase[T], IAggregate[T]):
8
+ class GroupBy[T: tp.Type[Table], *Ts, TProp](DecompositionQueryBase[T], IAggregate[T]):
9
9
  CLAUSE: str = "GROUP BY"
10
10
 
11
11
  def __init__(
12
12
  self,
13
13
  table: T,
14
- column: tp.Callable[[T], TProp],
14
+ column: tp.Callable[[T, *Ts], TProp],
15
15
  *,
16
16
  alias: bool = True,
17
17
  alias_name: str | None = None,
@@ -1,7 +1,9 @@
1
1
  from __future__ import annotations
2
- from typing import override, Callable, overload, Optional, TYPE_CHECKING
2
+ from collections import defaultdict
3
+ from typing import override, Callable, overload, Optional, TYPE_CHECKING, Type
3
4
 
4
5
 
6
+ from ormlambda.utils.module_tree.dfs_traversal import DFSTraversal
5
7
  from ormlambda.common.interfaces.IQueryCommand import IQuery
6
8
  from ormlambda import Disassembler
7
9
  from ormlambda import JoinType
@@ -21,6 +23,13 @@ class JoinSelector[TLeft, TRight](IQuery):
21
23
  "_compareop",
22
24
  )
23
25
 
26
+ @override
27
+ def __repr__(self) -> str:
28
+ table_col_left: str = f"{self._orig_table.__table_name__}.{self._left_col}"
29
+ table_col_right: str = f"{self._table_right.__table_name__}.{self._right_col}"
30
+
31
+ return f"{IQuery.__name__}: {self.__class__.__name__} ({table_col_left} == {table_col_right})"
32
+
24
33
  @overload
25
34
  def __init__(
26
35
  self,
@@ -102,3 +111,32 @@ class JoinSelector[TLeft, TRight](IQuery):
102
111
  right_col, # second_col
103
112
  ]
104
113
  return " ".join(list_)
114
+
115
+ @classmethod
116
+ def sort_join_selectors(cls, joins: set[JoinSelector]) -> tuple[JoinSelector]:
117
+ # FIXME [x]: How to sort when needed because it's not necessary at this point. It is for testing purpouse
118
+ if len(joins) == 1:
119
+ return tuple(joins)
120
+
121
+ join_object_map: dict[str, list[JoinSelector]] = defaultdict(list)
122
+
123
+ for obj in joins:
124
+ join_object_map[obj._orig_table].append(obj)
125
+
126
+ graph: dict[Type[Table], list[Type[Table]]] = defaultdict(list)
127
+ for join in joins:
128
+ graph[join._orig_table].append(join._table_right)
129
+
130
+ sorted_graph = DFSTraversal.sort(graph)[::-1]
131
+
132
+ if not sorted_graph:
133
+ return tuple(joins)
134
+
135
+ res = []
136
+ for table in sorted_graph:
137
+ tables = join_object_map[table]
138
+
139
+ if not tables:
140
+ continue
141
+ res.extend(tables)
142
+ return res
@@ -1,32 +1,37 @@
1
- from typing import override, Type, Callable, TYPE_CHECKING
1
+ from __future__ import annotations
2
+ from typing import override, Type, Callable, TYPE_CHECKING, Optional
2
3
 
3
4
  from ormlambda.common.abstract_classes.decomposition_query import DecompositionQueryBase
4
5
  from ormlambda.common.enums.join_type import JoinType
5
6
  from ormlambda.common.interfaces.IAggregate import IAggregate
6
7
  import shapely as shp
7
8
 
9
+
8
10
  if TYPE_CHECKING:
9
11
  from ormlambda import Table
12
+ from .joins import JoinSelector
10
13
 
11
14
 
12
- class Select[T: Type[Table]](DecompositionQueryBase[T]):
15
+ class Select[T: Type[Table], *Ts](DecompositionQueryBase[T, *Ts]):
13
16
  CLAUSE: str = "SELECT"
14
17
 
15
18
  def __init__(
16
19
  self,
17
- table: T,
20
+ tables: tuple[T, *Ts],
18
21
  lambda_query: Callable[[T], tuple] = lambda x: x,
19
22
  *,
20
23
  alias: bool = False,
21
24
  alias_name: str | None = None,
25
+ joins: Optional[list[JoinSelector]] = None,
22
26
  by: JoinType = JoinType.INNER_JOIN,
23
27
  ) -> None:
24
28
  super().__init__(
25
- table,
29
+ tables,
26
30
  lambda_query,
27
31
  alias=alias,
28
32
  alias_name=alias_name,
29
33
  by=by,
34
+ joins=joins,
30
35
  )
31
36
 
32
37
  # @classmethod
@@ -40,18 +45,16 @@ class Select[T: Type[Table]](DecompositionQueryBase[T]):
40
45
  cols: list[str] = []
41
46
  for x in self.all_clauses:
42
47
  if x.dtype is shp.Point:
43
- cols.append(x.concat_with_alias(f"ST_AsText({self._table.__table_name__}.{x.column})"))
48
+ cols.append(x.concat_with_alias(f"ST_AsText({self.table.__table_name__}.{x.column})"))
44
49
  else:
45
50
  cols.append(x.query)
46
51
 
47
52
  if isinstance(x._row_column, IAggregate) and x._row_column.has_foreign_keys:
48
- self._fk_relationship.update(x._row_column.fk_relationship)
53
+ self._joins.update(x._row_column.fk_relationship)
49
54
 
50
55
  col: str = ", ".join(cols)
51
- query: str = f"{self.CLAUSE} {col} FROM {self._table.__table_name__}"
52
- alias = ""
56
+ query: str = f"{self.CLAUSE} {col} FROM {self.table.__table_name__}"
53
57
 
54
- query += alias
55
58
  if self.has_foreign_keys:
56
59
  query += " " + self.stringify_foreign_key(" ")
57
60
 
@@ -22,13 +22,14 @@ if TYPE_CHECKING:
22
22
  from ormlambda import Table
23
23
  from src.ormlambda.databases.my_sql.clauses.select import Select
24
24
 
25
+ type TResponse[TFlavour, *Ts] = TFlavour | tuple[dict[str, tuple[*Ts]]] | tuple[tuple[*Ts]] | tuple[TFlavour]
26
+
25
27
 
26
28
  class Response[TFlavour, *Ts]:
27
- def __init__(self, response_values: list[tuple[*Ts]], columns: tuple[str], flavour: Type[TFlavour], model: Optional[Table] = None, select: Optional[Select] = None, **kwargs) -> None:
29
+ def __init__(self, response_values: list[tuple[*Ts]], columns: tuple[str], flavour: Type[TFlavour], model: Optional[Table] = None, select: Optional[Select] = None) -> None:
28
30
  self._response_values: list[tuple[*Ts]] = response_values
29
31
  self._columns: tuple[str] = columns
30
32
  self._flavour: Type[TFlavour] = flavour
31
- self._kwargs: dict[str, Any] = kwargs
32
33
  self._model: Table = model
33
34
  self._select: Select = select
34
35
 
@@ -47,23 +48,28 @@ class Response[TFlavour, *Ts]:
47
48
  def is_many(self) -> bool:
48
49
  return self._response_values_index > 1
49
50
 
50
- @property
51
- def response(self) -> tuple[dict[str, tuple[*Ts]]] | tuple[tuple[*Ts]] | tuple[TFlavour]:
51
+ def response(self, _tuple: bool, **kwargs) -> TResponse[TFlavour, *Ts]:
52
52
  if not self.is_there_response:
53
53
  return tuple([])
54
- clean_response = self._response_values
54
+ cleaned_response = self._response_values
55
+
55
56
  if self._select is not None:
56
- clean_response = self._parser_response()
57
- return tuple(self._cast_to_flavour(clean_response))
57
+ cleaned_response = self._parser_response()
58
+
59
+ cast_flavour = self._cast_to_flavour(cleaned_response, **kwargs)
60
+ if _tuple is not True:
61
+ return cast_flavour
58
62
 
59
- def _cast_to_flavour(self, data: list[tuple[*Ts]]) -> list[dict[str, tuple[*Ts]]] | list[tuple[*Ts]] | list[TFlavour]:
60
- def _dict() -> list[dict[str, tuple[*Ts]]]:
63
+ return tuple(cast_flavour)
64
+
65
+ def _cast_to_flavour(self, data: list[tuple[*Ts]], **kwargs) -> list[dict[str, tuple[*Ts]]] | list[tuple[*Ts]] | list[TFlavour]:
66
+ def _dict(**kwargs) -> list[dict[str, tuple[*Ts]]]:
61
67
  return [dict(zip(self._columns, x)) for x in data]
62
68
 
63
- def _tuple() -> list[tuple[*Ts]]:
69
+ def _tuple(**kwargs) -> list[tuple[*Ts]]:
64
70
  return data
65
71
 
66
- def _set() -> list[set]:
72
+ def _set(**kwargs) -> list[set]:
67
73
  for d in data:
68
74
  n = len(d)
69
75
  for i in range(n):
@@ -73,16 +79,20 @@ class Response[TFlavour, *Ts]:
73
79
  raise TypeError(f"unhashable type '{type(d[i])}' found in '{type(d)}' when attempting to cast the result into a '{set.__name__}' object")
74
80
  return [set(x) for x in data]
75
81
 
76
- def _default() -> list[TFlavour]:
77
- return [self._flavour(x, **self._kwargs) for x in data]
82
+ def _list(**kwargs) -> list[list]:
83
+ return [list(x) for x in data]
84
+
85
+ def _default(**kwargs) -> list[TFlavour]:
86
+ return self._flavour(data, **kwargs)
78
87
 
79
88
  selector: dict[Type[object], Any] = {
80
89
  dict: _dict,
81
90
  tuple: _tuple,
82
91
  set: _set,
92
+ list: _list,
83
93
  }
84
94
 
85
- return selector.get(self._flavour, _default)()
95
+ return selector.get(self._flavour, _default)(**kwargs)
86
96
 
87
97
  def _parser_response(self) -> TFlavour:
88
98
  new_response: list[list] = []
@@ -123,8 +133,7 @@ class MySQLRepository(IRepositoryBase[MySQLConnection]):
123
133
  def wrapper(self: MySQLRepository, *args, **kwargs):
124
134
  with self._pool.get_connection() as cnx:
125
135
  try:
126
- foo = func(self, cnx._cnx, *args, **kwargs)
127
- return foo
136
+ return func(self, cnx._cnx, *args, **kwargs)
128
137
  except Exception as e:
129
138
  cnx._cnx.rollback()
130
139
  raise e
@@ -140,7 +149,13 @@ class MySQLRepository(IRepositoryBase[MySQLConnection]):
140
149
 
141
150
  @override
142
151
  @get_connection
143
- def read_sql[TFlavour](self, cnx: MySQLConnection, query: str, flavour: Type[TFlavour] = tuple, **kwargs) -> tuple[TFlavour]:
152
+ def read_sql[TFlavour](
153
+ self,
154
+ cnx: MySQLConnection,
155
+ query: str,
156
+ flavour: Type[TFlavour] = tuple,
157
+ **kwargs,
158
+ ) -> tuple[TFlavour]:
144
159
  """
145
160
  Return tuple of tuples by default.
146
161
 
@@ -150,19 +165,15 @@ class MySQLRepository(IRepositoryBase[MySQLConnection]):
150
165
  - flavour: Type[TFlavour]: Useful to return tuple of any Iterable type as dict,set,list...
151
166
  """
152
167
 
153
- def get_and_drop_key(key: str) -> Optional[Any]:
154
- if key in kwargs:
155
- return kwargs.pop(key)
156
- return None
157
-
158
- model: Table = get_and_drop_key("model")
159
- select: Select = get_and_drop_key("select")
168
+ model: Table = kwargs.pop("model", None)
169
+ select: Select = kwargs.pop("select", None)
170
+ cast_to_tuple: bool = kwargs.pop("cast_to_tuple", True)
160
171
 
161
172
  with cnx.cursor(buffered=True) as cursor:
162
173
  cursor.execute(query)
163
174
  values: list[tuple] = cursor.fetchall()
164
175
  columns: tuple[str] = cursor.column_names
165
- return Response[TFlavour](model=model, response_values=values, columns=columns, flavour=flavour, select=select, **kwargs).response
176
+ return Response[TFlavour](model=model, response_values=values, columns=columns, flavour=flavour, select=select).response(_tuple=cast_to_tuple, **kwargs)
166
177
 
167
178
  # FIXME [ ]: this method does not comply with the implemented interface
168
179
  @get_connection