ormlambda 2.9.0__py3-none-any.whl → 2.10.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,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], Optional[JoinType]]
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,75 @@ 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, table: T1, relation: WhereCondition[T, T1], join_type: Optional[JoinType]) -> IStatements[T, T1]: ...
231
+ @overload
232
+ def join[T1](self, table: JoinCondition[T, T1]) -> IStatements[T, T1]: ...
233
+ @overload
234
+ def join[T1](self, table: TupleJoins1[T, T1]) -> IStatements[T, T1]: ...
235
+ @overload
236
+ def join[T1, T2](self, table: TupleJoins2[T, T1, T2]) -> IStatements[T, T1, T2]: ...
237
+ @overload
238
+ def join[T1, T2, T3](self, table: TupleJoins3[T, T1, T2, T3]) -> IStatements[T, T1, T2, T3]: ...
239
+ @overload
240
+ def join[T1, T2, T3, T4](self, table: TupleJoins4[T, T1, T2, T3, T4]) -> IStatements[T, T1, T2, T3, T4]: ...
241
+ @overload
242
+ def join[T1, T2, T3, T4, T5](self, table: TupleJoins5[T, T1, T2, T3, T4, T5]) -> IStatements[T, T1, T2, T3, T4, T5]: ...
243
+ @overload
244
+ def join[T1, T2, T3, T4, T5, T6](self, table: TupleJoins6[T, T1, T2, T3, T4, T5, T6]) -> IStatements[T, T1, T2, T3, T4, T5, T6]: ...
245
+
246
+ @abstractmethod
247
+ def join[*FKTables](
248
+ self,
249
+ table: Optional[T] = ...,
250
+ relation: Optional[WhereCondition[T, FKTables]] = ...,
251
+ join_type: Optional[JoinType] = ...,
252
+ ) -> IStatements[T, *FKTables]: ...
253
+
203
254
  # endregion
204
255
  # region select
205
256
  @overload
206
- def select(self) -> tuple[T, ...]: ...
257
+ def select(self) -> SelectRes1[T]: ...
207
258
  @overload
208
- def select[T1](self, selector: Callable[[T], T1], *, by: Optional[Enum] = ...) -> tuple[tuple[T1, ...]]: ...
259
+ def select[T1](self, selector: Callable[[T], T1], *, by: Optional[Enum] = ...) -> SelectRes1[T1]: ...
209
260
  @overload
210
- def select[T1](self, selector: Callable[[T], tuple[T1]], *, by: Optional[Enum] = ...) -> tuple[tuple[T1, ...]]: ...
261
+ def select[T1](self, selector: Callable[[T], tuple[T1]], *, by: Optional[Enum] = ...) -> SelectRes1[T1]: ...
211
262
  @overload
212
- def select[T1, T2](self, selector: Callable[[T], tuple[T1, T2]], *, by: Optional[Enum] = ...) -> tuple[tuple[T1, ...], tuple[T2, ...]]: ...
263
+ def select[T1, T2](self, selector: Callable[[T, *Ts], tuple[T1, T2]], *, by: Optional[Enum] = ...) -> SelectRes2[T1, T2]: ...
213
264
  @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, ...]]: ...
265
+ def select[T1, T2, T3](self, selector: Callable[[T, *Ts], tuple[T1, T2, T3]], *, by: Optional[Enum] = ...) -> SelectRes3[T1, T2, T3]: ...
215
266
  @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, ...]]: ...
267
+ 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
268
  @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, ...]]: ...
269
+ 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
270
  @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, ...]]: ...
271
+ 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
272
  @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, ...]]: ...
273
+ 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
274
  @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, ...]]: ...
275
+ 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
276
  @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, ...]]: ...
277
+ 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
278
  @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, ...]]: ...
279
+ 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]: ...
280
+
281
+ @overload
282
+ def select[TFlavour](self, selector: Optional[Callable[[T, *Ts], tuple]] = ..., *, cast_to_tuple: bool = ..., flavour: Type[TFlavour], by: Optional[Enum] = ..., **kwargs) -> TFlavour: ...
231
283
  @overload
232
- def select[Ts](self, selector: Optional[Callable[[T], Ts]] = ..., *, flavour: Type[tuple], by: Optional[Enum] = ...) -> tuple[Ts, ...]: ...
284
+ def select[TRes](self, selector: Optional[Callable[[T, *Ts], TRes]] = ..., *, flavour: Type[tuple], by: Optional[Enum] = ..., **kwargs) -> tuple[TRes, ...]: ...
233
285
  @overload
234
- def select[Ts](self, selector: Optional[Callable[[T], tuple[Ts]]] = ..., *, flavour: Type[tuple], by: Optional[Enum] = ...) -> tuple[Ts, ...]: ...
286
+ def select[TRes](self, selector: Optional[Callable[[T, *Ts], tuple[TRes]]] = ..., *, flavour: Type[tuple], by: Optional[Enum] = ..., **kwargs) -> tuple[TRes, ...]: ...
235
287
  @overload
236
- def select[*Ts](self, selector: Optional[Callable[[T], tuple[*Ts]]] = ..., *, flavour: Type[tuple], by: Optional[Enum] = ...) -> tuple[tuple[*Ts]]: ...
288
+ def select[*TRes](self, selector: Optional[Callable[[T, *Ts], tuple[*TRes]]] = ..., *, flavour: Type[tuple], by: Optional[Enum] = ..., **kwargs) -> tuple[tuple[*TRes]]: ...
237
289
  @overload
238
- def select[TFlavour](self, selector: Optional[Callable[[T], tuple]] = ..., *, flavour: Type[TFlavour], by: Optional[Enum] = ...) -> tuple[TFlavour]: ...
290
+ def select[TFlavour](self, selector: Optional[Callable[[T, *Ts], tuple]] = ..., *, flavour: Type[TFlavour], by: Optional[Enum] = ..., **kwargs) -> tuple[TFlavour]: ...
291
+
239
292
  @abstractmethod
240
- def select[TValue, TFlavour, *Ts](self, selector: Optional[Callable[[T], tuple[TValue, *Ts]]] = ..., *, flavour: Type[TFlavour] = ..., by: JoinType = ...): ...
293
+ 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
294
 
242
295
  # endregion
243
296
  # region select_one
@@ -246,24 +299,24 @@ class IStatements[T: Table](ABC):
246
299
  @overload
247
300
  def select_one[TFlavour](self, *, by: Optional[Enum] = ..., flavour: Type[TFlavour]) -> TFlavour: ...
248
301
  @overload
249
- def select_one[T1](self, selector: Callable[[T], tuple[T1]], *, by: Optional[Enum] = ...) -> T1: ...
302
+ def select_one[T1](self, selector: Callable[[T, *Ts], tuple[T1]], *, by: Optional[Enum] = ...) -> T1: ...
250
303
  @overload
251
- def select_one[*Ts](self, selector: Callable[[T], tuple[*Ts]], *, by: Optional[Enum] = ...) -> tuple[*Ts]: ...
304
+ def select_one[*TRes](self, selector: Callable[[T, *Ts], tuple[*TRes]], *, by: Optional[Enum] = ...) -> tuple[*TRes]: ...
252
305
  @overload
253
- def select_one[T1](self, selector: Callable[[T], tuple[T1]], *, by: Optional[Enum] = ..., flavour: Type) -> T1: ...
306
+ def select_one[T1](self, selector: Callable[[T, *Ts], tuple[T1]], *, by: Optional[Enum] = ..., flavour: Type) -> T1: ...
254
307
  @overload
255
- def select_one[T1, TFlavour](self, selector: Callable[[T], T1], *, by: Optional[Enum] = ..., flavour: Type[TFlavour]) -> T1: ...
308
+ def select_one[T1, TFlavour](self, selector: Callable[[T, *Ts], T1], *, by: Optional[Enum] = ..., flavour: Type[TFlavour]) -> T1: ...
256
309
  @overload
257
- def select_one[*Ts](self, selector: Callable[[T], tuple[*Ts]], *, by: Optional[Enum] = ..., flavour: Type[tuple]) -> tuple[*Ts]: ...
310
+ def select_one[*Ts](self, selector: Callable[[T, *Ts], tuple[*Ts]], *, by: Optional[Enum] = ..., flavour: Type[tuple]) -> tuple[*Ts]: ...
258
311
  @overload
259
- def select_one[TFlavour](self, selector: Callable[[T], tuple], *, by: Optional[Enum] = ..., flavour: Type[TFlavour]) -> TFlavour: ...
312
+ def select_one[TFlavour](self, selector: Callable[[T, *Ts], tuple], *, by: Optional[Enum] = ..., flavour: Type[TFlavour]) -> TFlavour: ...
260
313
  @abstractmethod
261
- def select_one[TValue, TFlavour, *Ts](self, selector: Optional[Callable[[T], tuple[TValue, *Ts]]] = lambda: None, *, flavour: Type[TFlavour] = ..., by: Optional[Enum] = ...): ...
314
+ def select_one[TValue, TFlavour, *Ts](self, selector: Optional[Callable[[T, *Ts], tuple[TValue, *Ts]]] = lambda: None, *, flavour: Type[TFlavour] = ..., by: Optional[Enum] = ...): ...
262
315
 
263
316
  # endregion
264
317
  # region group_by
265
318
  @abstractmethod
266
- def group_by[TRepo, *Ts](self, column: Callable[[T], TRepo]) -> IStatements[T]: ...
319
+ def group_by[TRepo](self, column: Callable[[T, *Ts], TRepo]) -> IStatements[T, *Ts]: ...
267
320
 
268
321
  # endregion
269
322
 
@@ -271,7 +324,16 @@ class IStatements[T: Table](ABC):
271
324
  def _build(self) -> str: ...
272
325
 
273
326
 
274
- class IStatements_two_generic[T: Table, TRepo](IStatements[T]):
327
+ class IStatements_two_generic[T: Table, *Ts, TRepo](IStatements[T, *Ts]):
275
328
  @property
276
329
  @abstractmethod
277
330
  def repository(self) -> IRepositoryBase[TRepo]: ...
331
+
332
+ @property
333
+ def query(self) -> str: ...
334
+
335
+ @property
336
+ def model(self) -> Type[T]: ...
337
+
338
+ @property
339
+ 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