ormlambda 3.11.2__py3-none-any.whl → 3.12.2__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.
Files changed (28) hide show
  1. ormlambda/__init__.py +1 -1
  2. ormlambda/common/abstract_classes/clause_info_converter.py +73 -0
  3. ormlambda/common/abstract_classes/decomposition_query.py +12 -68
  4. ormlambda/common/abstract_classes/non_query_base.py +2 -2
  5. ormlambda/common/interfaces/ICustomAlias.py +1 -1
  6. ormlambda/components/delete/abstract_delete.py +2 -2
  7. ormlambda/components/join/__init__.py +1 -0
  8. ormlambda/databases/my_sql/clauses/drop_table.py +8 -5
  9. ormlambda/databases/my_sql/clauses/group_by.py +1 -2
  10. ormlambda/databases/my_sql/clauses/select.py +2 -0
  11. ormlambda/databases/my_sql/clauses/upsert.py +8 -4
  12. ormlambda/databases/my_sql/query_builder.py +158 -0
  13. ormlambda/databases/my_sql/statements.py +18 -156
  14. ormlambda/engine/url.py +4 -1
  15. ormlambda/sql/clause_info/__init__.py +2 -1
  16. ormlambda/sql/clause_info/aggregate_function_base.py +86 -0
  17. ormlambda/sql/clause_info/clause_info.py +1 -98
  18. ormlambda/sql/clause_info/interface/IClauseInfo.py +37 -0
  19. ormlambda/sql/clause_info/interface/__init__.py +1 -0
  20. ormlambda/sql/column.py +3 -0
  21. ormlambda/statements/base_statement.py +6 -2
  22. ormlambda/statements/interfaces/IStatements.py +21 -18
  23. ormlambda/utils/module_tree/dynamic_module.py +3 -2
  24. {ormlambda-3.11.2.dist-info → ormlambda-3.12.2.dist-info}/METADATA +56 -10
  25. {ormlambda-3.11.2.dist-info → ormlambda-3.12.2.dist-info}/RECORD +28 -23
  26. /ormlambda/{databases/my_sql → components/join}/join_context.py +0 -0
  27. {ormlambda-3.11.2.dist-info → ormlambda-3.12.2.dist-info}/LICENSE +0 -0
  28. {ormlambda-3.11.2.dist-info → ormlambda-3.12.2.dist-info}/WHEEL +0 -0
@@ -1,12 +1,9 @@
1
1
  from __future__ import annotations
2
- from typing import Concatenate, Iterable, TypedDict, override, Type, TYPE_CHECKING, Any, Callable, Optional
2
+ from typing import Concatenate, Iterable, override, Type, TYPE_CHECKING, Any, Callable, Optional
3
3
  from mysql.connector import errors, errorcode
4
4
 
5
- from ormlambda.sql.clause_info.clause_info_context import ClauseInfoContext
6
- from ormlambda.databases.my_sql.clauses.joins import JoinSelector
7
5
  from ormlambda import ForeignKey
8
6
 
9
- from ormlambda.common.interfaces import IQuery
10
7
  from mysql.connector import MySQLConnection
11
8
 
12
9
 
@@ -16,9 +13,9 @@ if TYPE_CHECKING:
16
13
  from ormlambda.statements.types import OrderTypes
17
14
  from ormlambda.sql.types import ColumnType
18
15
  from ormlambda.statements.types import SelectCols
19
- from ormlambda.repository.interfaces import IRepositoryBase
16
+ from ormlambda.repository import BaseRepository
20
17
  from ormlambda.statements.interfaces import IStatements_two_generic
21
- from ormlambda.repository.interfaces.IRepositoryBase import TypeExists
18
+ from ormlambda.statements.types import TypeExists
22
19
  from ormlambda.sql.clause_info import IAggregate
23
20
  from ormlambda.statements.types import WhereTypes
24
21
 
@@ -44,8 +41,10 @@ from .clauses import Alias
44
41
  from ormlambda import Table, Column
45
42
  from ormlambda.common.enums import JoinType
46
43
  from . import functions as func
47
- from .join_context import JoinContext, TupleJoinType
44
+ from ormlambda.components.join import JoinContext, TupleJoinType
45
+
48
46
  from ormlambda.common.global_checker import GlobalChecker
47
+ from .query_builder import QueryBuilder
49
48
 
50
49
 
51
50
  # COMMENT: It's so important to prevent information generated by other tests from being retained in the class.
@@ -63,152 +62,11 @@ def clear_list[T, **P](f: Callable[Concatenate[MySQLStatements, P], T]) -> Calla
63
62
  return wrapper
64
63
 
65
64
 
66
- class OrderType(TypedDict):
67
- Select: Select
68
- JoinSelector: JoinSelector
69
- Where: Where
70
- Order: Order
71
- GroupBy: GroupBy
72
- Having: Having
73
- Limit: Limit
74
- Offset: Offset
75
-
76
-
77
- class QueryBuilder(IQuery):
78
- __order__: tuple[str, ...] = ("Select", "JoinSelector", "Where", "GroupBy", "Having", "Order", "Limit", "Offset")
79
-
80
- def __init__(self, by: JoinType = JoinType.INNER_JOIN):
81
- self._context = ClauseInfoContext()
82
- self._query_list: OrderType = {}
83
- self._by = by
84
-
85
- self._joins: Optional[IQuery] = None
86
- self._select: Optional[IQuery] = None
87
- self._where: Optional[IQuery] = None
88
- self._order: Optional[IQuery] = None
89
- self._group_by: Optional[IQuery] = None
90
- self._limit: Optional[IQuery] = None
91
- self._offset: Optional[IQuery] = None
92
-
93
- def add_statement[T](self, clause: ClauseInfo[T]):
94
- self.update_context(clause)
95
- self._query_list[type(clause).__name__] = clause
96
-
97
- @property
98
- def by(self) -> JoinType:
99
- return self._by
100
-
101
- @by.setter
102
- def by(self, value: JoinType) -> None:
103
- self._by = value
104
-
105
- @property
106
- def JOINS(self) -> Optional[set[JoinSelector]]:
107
- return self._joins
108
-
109
- @property
110
- def SELECT(self) -> IQuery:
111
- return self._query_list.get("Select", None)
112
-
113
- @property
114
- def WHERE(self) -> IQuery:
115
- where = self._query_list.get("Where", None)
116
- if not isinstance(where, Iterable):
117
- if not where:
118
- return ()
119
- return (where,)
120
- return where
121
-
122
- @property
123
- def ORDER(self) -> IQuery:
124
- return self._query_list.get("Order", None)
125
-
126
- @property
127
- def GROUP_BY(self) -> IQuery:
128
- return self._query_list.get("GroupBy", None)
129
-
130
- @property
131
- def HAVING(self) -> IQuery:
132
- where = self._query_list.get("Having", None)
133
- if not isinstance(where, Iterable):
134
- if not where:
135
- return ()
136
- return (where,)
137
- return where
138
-
139
- @property
140
- def LIMIT(self) -> IQuery:
141
- return self._query_list.get("Limit", None)
142
-
143
- @property
144
- def OFFSET(self) -> IQuery:
145
- return self._query_list.get("Offset", None)
146
-
147
- @property
148
- def query(self) -> str:
149
- # COMMENT: (select.query, query)We must first create an alias for 'FROM' and then define all the remaining clauses.
150
- # This order is mandatory because it adds the clause name to the context when accessing the .query property of 'FROM'
151
-
152
- extract_joins = self.pop_tables_and_create_joins_from_ForeignKey(self._by)
153
-
154
- JOINS = self.stringify_foreign_key(extract_joins, " ")
155
- query_list: tuple[str, ...] = (
156
- self.SELECT.query,
157
- JOINS,
158
- Where.join_condition(self.WHERE, True, self._context) if self.WHERE else None,
159
- self.GROUP_BY.query if self.GROUP_BY else None,
160
- Having.join_condition(self.HAVING, True, self._context) if self.HAVING else None,
161
- self.ORDER.query if self.ORDER else None,
162
- self.LIMIT.query if self.LIMIT else None,
163
- self.OFFSET.query if self.OFFSET else None,
164
- )
165
- return " ".join([x for x in query_list if x])
166
-
167
- def stringify_foreign_key(self, joins: set[JoinSelector], sep: str = "\n") -> Optional[str]:
168
- if not joins:
169
- return None
170
- sorted_joins = JoinSelector.sort_join_selectors(joins)
171
- return f"{sep}".join([join.query for join in sorted_joins])
172
-
173
- def pop_tables_and_create_joins_from_ForeignKey(self, by: JoinType = JoinType.INNER_JOIN) -> set[JoinSelector]:
174
- # When we applied filters in any table that we wont select any column, we need to add manually all neccessary joins to achieve positive result.
175
- if not ForeignKey.stored_calls:
176
- return None
177
-
178
- joins = set()
179
- # Always it's gonna be a set of two
180
- # FIXME [x]: Resolved when we get Compare object instead ClauseInfo. For instance, when we have multiples condition using '&' or '|'
181
- for fk in ForeignKey.stored_calls.copy():
182
- fk = ForeignKey.stored_calls.pop(fk)
183
- self._context._add_table_alias(fk.tright, fk.alias)
184
- join = JoinSelector(fk.resolved_function(self._context), by, context=self._context, alias=fk.alias)
185
- joins.add(join)
186
-
187
- return joins
188
-
189
- def clear(self) -> None:
190
- self.__init__()
191
- return None
192
-
193
- def update_context(self, clause: ClauseInfo) -> None:
194
- if not hasattr(clause, "context"):
195
- return None
196
-
197
- if clause.context is not None:
198
- self._context.update(clause.context)
199
- clause.context = self._context
200
-
201
-
202
65
  class MySQLStatements[T: Table, *Ts](BaseStatement[T, MySQLConnection]):
203
- def __init__(self, model: tuple[T, *Ts], repository: IRepositoryBase) -> None:
66
+ def __init__(self, model: tuple[T, *Ts], repository: BaseRepository) -> None:
204
67
  super().__init__(model, repository=repository)
205
68
  self._query_builder = QueryBuilder()
206
69
 
207
- @property
208
- @override
209
- def repository(self) -> IRepositoryBase:
210
- return self._repository
211
-
212
70
  @override
213
71
  def create_table(self, if_exists: TypeExists = "fail") -> None:
214
72
  name: str = self._model.__table_name__
@@ -232,10 +90,6 @@ class MySQLStatements[T: Table, *Ts](BaseStatement[T, MySQLConnection]):
232
90
  self._repository.execute(query)
233
91
  return None
234
92
 
235
- @override
236
- def table_exists(self) -> bool:
237
- return self._repository.table_exists(self._model.__table_name__)
238
-
239
93
  @override
240
94
  @clear_list
241
95
  def insert(self, instances: T | list[T]) -> None:
@@ -293,7 +147,7 @@ class MySQLStatements[T: Table, *Ts](BaseStatement[T, MySQLConnection]):
293
147
  @override
294
148
  def count[TProp](
295
149
  self,
296
- selection: None | SelectCols[T,TProp] = lambda x: "*",
150
+ selection: None | SelectCols[T, TProp] = lambda x: "*",
297
151
  alias="count",
298
152
  execute: bool = False,
299
153
  ) -> Optional[int]:
@@ -386,6 +240,9 @@ class MySQLStatements[T: Table, *Ts](BaseStatement[T, MySQLConnection]):
386
240
  by: JoinType = JoinType.INNER_JOIN,
387
241
  **kwargs,
388
242
  ):
243
+ if "alias" in kwargs:
244
+ alias = kwargs.pop("alias")
245
+ kwargs["alias_clause"] = alias
389
246
  select_clause = GlobalChecker.resolved_callback_object(selector, self._models)
390
247
 
391
248
  if selector is None:
@@ -400,6 +257,7 @@ class MySQLStatements[T: Table, *Ts](BaseStatement[T, MySQLConnection]):
400
257
  select = Select[T, *Ts](
401
258
  self._models,
402
259
  columns=select_clause,
260
+ **kwargs,
403
261
  )
404
262
  self._query_builder.add_statement(select)
405
263
 
@@ -458,14 +316,18 @@ class MySQLStatements[T: Table, *Ts](BaseStatement[T, MySQLConnection]):
458
316
  )
459
317
 
460
318
  @override
461
- def groupby[TProp](self, column: ColumnType[TProp] | Callable[[T, *Ts], Any]):
319
+ def groupby[TProp](self, column: ColumnType[TProp] | Callable[[T, *Ts], Any]) -> IStatements_two_generic[T]:
320
+ column = GlobalChecker.resolved_callback_object(column, self.models)
321
+
462
322
  groupby = GroupBy(column=column, context=self._query_builder._context)
463
323
  # Only can be one LIMIT SQL parameter. We only use the last LimitQuery
464
324
  self._query_builder.add_statement(groupby)
465
325
  return self
466
326
 
467
327
  @override
468
- def alias[TProp](self, column: ColumnType[TProp], alias: AliasType[ClauseInfo[T]]) -> ClauseInfo[T]:
328
+ def alias[TProp](self, column: SelectCols[T, TProp], alias: AliasType[ClauseInfo[T]]) -> ClauseInfo[T]:
329
+ column = GlobalChecker.resolved_callback_object(column, self.models)
330
+
469
331
  return Alias(
470
332
  table=column.table,
471
333
  column=column,
ormlambda/engine/url.py CHANGED
@@ -1,6 +1,7 @@
1
1
  """
2
2
  URL class extracted from SQLAlchemy
3
3
  """
4
+
4
5
  from __future__ import annotations
5
6
 
6
7
  import re
@@ -27,7 +28,9 @@ from urllib.parse import (
27
28
 
28
29
  from . import utils
29
30
 
30
- type DrivernameType = Literal['mysql'] | str
31
+ type DrivernameType = Literal["mysql"] | str
32
+
33
+
31
34
  class URL(NamedTuple):
32
35
  """
33
36
  Represent the components of a URL used to connect to a database.
@@ -1,3 +1,4 @@
1
1
  from .interface import IAggregate # noqa: F401
2
- from .clause_info import ClauseInfo, AggregateFunctionBase # noqa: F401
2
+ from .clause_info import ClauseInfo # noqa: F401
3
+ from .aggregate_function_base import AggregateFunctionBase # noqa: F401
3
4
  from .clause_info_context import ClauseContextType, ClauseInfoContext # noqa: F401
@@ -0,0 +1,86 @@
1
+ from __future__ import annotations
2
+ import abc
3
+ import typing as tp
4
+
5
+ from ormlambda import Table
6
+ from ormlambda import Column
7
+ from ormlambda.sql.types import (
8
+ TableType,
9
+ ColumnType,
10
+ AliasType,
11
+ )
12
+ from .interface import IAggregate
13
+ from ormlambda.common.errors import NotKeysInIAggregateError
14
+ from ormlambda.sql import ForeignKey
15
+ from ormlambda.sql.table import TableMeta
16
+ from .clause_info import ClauseInfo
17
+ from .clause_info_context import ClauseContextType
18
+
19
+
20
+ class AggregateFunctionBase[T: Table](ClauseInfo[T], IAggregate):
21
+ def __init__[TProp: Column](
22
+ self,
23
+ table: TableType[T],
24
+ column: tp.Optional[ColumnType[TProp]] = None,
25
+ alias_table: tp.Optional[AliasType[ClauseInfo[T]]] = None,
26
+ alias_clause: tp.Optional[AliasType[ClauseInfo[T]]] = None,
27
+ context: ClauseContextType = None,
28
+ keep_asterisk: bool = False,
29
+ preserve_context: bool = False,
30
+ ):
31
+ self._alias_aggregate = alias_clause
32
+ super().__init__(
33
+ table=table,
34
+ column=column,
35
+ alias_table=alias_table,
36
+ context=context,
37
+ keep_asterisk=keep_asterisk,
38
+ preserve_context=preserve_context,
39
+ )
40
+
41
+ @staticmethod
42
+ @abc.abstractmethod
43
+ def FUNCTION_NAME() -> str: ...
44
+
45
+ @classmethod
46
+ def _convert_into_clauseInfo[TypeColumns, TProp](cls, columns: ClauseInfo | ColumnType[TProp], context: ClauseContextType) -> list[ClauseInfo]:
47
+ type DEFAULT = tp.Literal["default"]
48
+ type ClusterType = ColumnType | ForeignKey | DEFAULT
49
+
50
+ dicc_type: dict[ClusterType, tp.Callable[[ClusterType], ClauseInfo]] = {
51
+ Column: lambda column: ClauseInfo(column.table, column, context=context),
52
+ ClauseInfo: lambda column: column,
53
+ ForeignKey: lambda tbl: ClauseInfo(tbl.tright, tbl.tright, context=context),
54
+ TableMeta: lambda tbl: ClauseInfo(tbl, tbl, context=context),
55
+ "default": lambda column: ClauseInfo(table=None, column=column, context=context),
56
+ }
57
+ all_clauses: list[ClauseInfo] = []
58
+ if isinstance(columns, str) or not isinstance(columns, tp.Iterable):
59
+ columns = (columns,)
60
+ for value in columns:
61
+ all_clauses.append(dicc_type.get(type(value), dicc_type["default"])(value))
62
+
63
+ return all_clauses
64
+
65
+ @tp.override
66
+ @property
67
+ def query(self) -> str:
68
+ wrapped_ci = self.wrapped_clause_info(self)
69
+ if not self._alias_aggregate:
70
+ return wrapped_ci
71
+
72
+ return ClauseInfo(
73
+ table=None,
74
+ column=wrapped_ci,
75
+ alias_clause=self._alias_aggregate,
76
+ context=self._context,
77
+ keep_asterisk=self._keep_asterisk,
78
+ preserve_context=self._preserve_context,
79
+ ).query
80
+
81
+ def wrapped_clause_info(self, ci: ClauseInfo[T]) -> str:
82
+ # avoid use placeholder when using IAggregate because no make sense.
83
+ if self._alias_aggregate and (found := self._keyRegex.findall(self._alias_aggregate)):
84
+ raise NotKeysInIAggregateError(found)
85
+
86
+ return f"{self.FUNCTION_NAME()}({ci._create_query()})"
@@ -1,21 +1,17 @@
1
1
  from __future__ import annotations
2
- import abc
3
2
  import typing as tp
4
3
  import re
5
4
 
6
5
  from ormlambda import Table
7
6
  from ormlambda import Column
8
- from ormlambda.common.interfaces.IQueryCommand import IQuery
9
7
  from ormlambda.sql.types import (
10
8
  ASTERISK,
11
9
  TableType,
12
10
  ColumnType,
13
11
  AliasType,
14
12
  )
15
- from .interface import IAggregate
16
- from ormlambda.common.errors import NotKeysInIAggregateError
13
+ from .interface import IClauseInfo
17
14
  from ormlambda.sql import ForeignKey
18
- from ormlambda.sql.table import TableMeta
19
15
  from ormlambda.caster import Caster
20
16
 
21
17
 
@@ -32,30 +28,6 @@ class ReplacePlaceholderError(ValueError):
32
28
  return "You cannot use {" + self.placeholder + "} placeholder without using '" + self.attr + "' attribute"
33
29
 
34
30
 
35
- class IClauseInfo[T: Table](IQuery):
36
- @property
37
- @abc.abstractmethod
38
- def table(self) -> TableType[T]: ...
39
- @property
40
- @abc.abstractmethod
41
- def alias_clause(self) -> tp.Optional[str]: ...
42
- @property
43
- @abc.abstractmethod
44
- def alias_table(self) -> tp.Optional[str]: ...
45
- @property
46
- @abc.abstractmethod
47
- def column(self) -> str: ...
48
- @property
49
- @abc.abstractmethod
50
- def unresolved_column(self) -> ColumnType: ...
51
- @property
52
- @abc.abstractmethod
53
- def context(self) -> ClauseContextType: ...
54
- @property
55
- @abc.abstractmethod
56
- def dtype[TProp](self) -> tp.Optional[tp.Type[TProp]]: ...
57
-
58
-
59
31
  class ClauseInfo[T: Table](IClauseInfo[T]):
60
32
  _keyRegex: re.Pattern = re.compile(r"{([^{}:]+)}")
61
33
 
@@ -363,72 +335,3 @@ class ClauseInfo[T: Table](IClauseInfo[T]):
363
335
  if isinstance(data, Column):
364
336
  return True
365
337
  return False
366
-
367
-
368
- class AggregateFunctionBase[T: Table](ClauseInfo[T], IAggregate):
369
- def __init__[TProp: Column](
370
- self,
371
- table: TableType[T],
372
- column: tp.Optional[ColumnType[TProp]] = None,
373
- alias_table: tp.Optional[AliasType[ClauseInfo[T]]] = None,
374
- alias_clause: tp.Optional[AliasType[ClauseInfo[T]]] = None,
375
- context: ClauseContextType = None,
376
- keep_asterisk: bool = False,
377
- preserve_context: bool = False,
378
- ):
379
- self._alias_aggregate = alias_clause
380
- super().__init__(
381
- table=table,
382
- column=column,
383
- alias_table=alias_table,
384
- context=context,
385
- keep_asterisk=keep_asterisk,
386
- preserve_context=preserve_context,
387
- )
388
-
389
- @staticmethod
390
- @abc.abstractmethod
391
- def FUNCTION_NAME() -> str: ...
392
-
393
- @classmethod
394
- def _convert_into_clauseInfo[TypeColumns, TProp](cls, columns: ClauseInfo | ColumnType[TProp], context: ClauseContextType) -> list[ClauseInfo]:
395
- type DEFAULT = tp.Literal["default"]
396
- type ClusterType = ColumnType | ForeignKey | DEFAULT
397
-
398
- dicc_type: dict[ClusterType, tp.Callable[[ClusterType], ClauseInfo]] = {
399
- Column: lambda column: ClauseInfo(column.table, column, context=context),
400
- ClauseInfo: lambda column: column,
401
- ForeignKey: lambda tbl: ClauseInfo(tbl.tright, tbl.tright, context=context),
402
- TableMeta: lambda tbl: ClauseInfo(tbl, tbl, context=context),
403
- "default": lambda column: ClauseInfo(table=None, column=column, context=context),
404
- }
405
- all_clauses: list[ClauseInfo] = []
406
- if isinstance(columns, str) or not isinstance(columns, tp.Iterable):
407
- columns = (columns,)
408
- for value in columns:
409
- all_clauses.append(dicc_type.get(type(value), dicc_type["default"])(value))
410
-
411
- return all_clauses
412
-
413
- @tp.override
414
- @property
415
- def query(self) -> str:
416
- wrapped_ci = self.wrapped_clause_info(self)
417
- if not self._alias_aggregate:
418
- return wrapped_ci
419
-
420
- return ClauseInfo(
421
- table=None,
422
- column=wrapped_ci,
423
- alias_clause=self._alias_aggregate,
424
- context=self._context,
425
- keep_asterisk=self._keep_asterisk,
426
- preserve_context=self._preserve_context,
427
- ).query
428
-
429
- def wrapped_clause_info(self, ci: ClauseInfo[T]) -> str:
430
- # avoid use placeholder when using IAggregate because no make sense.
431
- if self._alias_aggregate and (found := self._keyRegex.findall(self._alias_aggregate)):
432
- raise NotKeysInIAggregateError(found)
433
-
434
- return f"{self.FUNCTION_NAME()}({ci._create_query()})"
@@ -0,0 +1,37 @@
1
+ from __future__ import annotations
2
+ import abc
3
+ import typing as tp
4
+
5
+ from ormlambda import Table
6
+ from ormlambda.common.interfaces.IQueryCommand import IQuery
7
+ from ormlambda.sql.types import (
8
+ TableType,
9
+ ColumnType,
10
+ )
11
+
12
+
13
+ from ..clause_info_context import ClauseContextType
14
+
15
+
16
+ class IClauseInfo[T: Table](IQuery):
17
+ @property
18
+ @abc.abstractmethod
19
+ def table(self) -> TableType[T]: ...
20
+ @property
21
+ @abc.abstractmethod
22
+ def alias_clause(self) -> tp.Optional[str]: ...
23
+ @property
24
+ @abc.abstractmethod
25
+ def alias_table(self) -> tp.Optional[str]: ...
26
+ @property
27
+ @abc.abstractmethod
28
+ def column(self) -> str: ...
29
+ @property
30
+ @abc.abstractmethod
31
+ def unresolved_column(self) -> ColumnType: ...
32
+ @property
33
+ @abc.abstractmethod
34
+ def context(self) -> ClauseContextType: ...
35
+ @property
36
+ @abc.abstractmethod
37
+ def dtype[TProp](self) -> tp.Optional[tp.Type[TProp]]: ...
@@ -1 +1,2 @@
1
1
  from .IAggregate import IAggregate # noqa: F401
2
+ from .IClauseInfo import IClauseInfo # noqa: F401
ormlambda/sql/column.py CHANGED
@@ -128,6 +128,9 @@ class Column[TProp]:
128
128
  return self.__comparer_creator(other, ConditionType.GREATER_THAN_OR_EQUAL.value, *args)
129
129
 
130
130
  def contains[LTable, OTherTable, OtherProp](self, other: ColumnType[OtherProp], *args) -> Comparer[LTable, TProp, OTherTable, OtherProp]:
131
+ if not isinstance(other, tuple) and isinstance(other, Iterable):
132
+ other = tuple(other)
133
+
131
134
  return self.__comparer_creator(other, ConditionType.IN.value, *args)
132
135
 
133
136
  def not_contains[LTable, OTherTable, OtherProp](self, other: ColumnType[OtherProp], *args) -> Comparer[LTable, TProp, OTherTable, OtherProp]:
@@ -33,6 +33,10 @@ class BaseStatement[T: Table, TRepo](IStatements_two_generic[T, TRepo]):
33
33
  # Si no heredase de Table no sabriamos identificar el tipo de dato del que se trata porque al llamar a isinstance, obtendriamos el nombre de la clase que mapea a la tabla, Encargo, Edificio, Presupuesto y no podriamos crear una clase generica
34
34
  raise Exception(f"'{model}' class does not inherit from Table class")
35
35
 
36
+ @override
37
+ def table_exists(self) -> bool:
38
+ return self._repository.table_exists(self._model.__table_name__)
39
+
36
40
  @staticmethod
37
41
  def __valid_repository(repository: Any) -> bool:
38
42
  if not isinstance(repository, BaseRepository):
@@ -67,8 +71,8 @@ class BaseStatement[T: Table, TRepo](IStatements_two_generic[T, TRepo]):
67
71
  return self._models
68
72
 
69
73
  @property
70
- @override
71
- def repository(self) -> BaseRepository: ...
74
+ def repository(self) -> BaseRepository:
75
+ return self._repository
72
76
 
73
77
 
74
78
  class ClusterQuery[T]:
@@ -4,13 +4,12 @@ from enum import Enum
4
4
  from abc import abstractmethod, ABC
5
5
 
6
6
 
7
-
8
7
  if TYPE_CHECKING:
9
8
  from ormlambda.repository import BaseRepository
10
9
  from ormlambda import Table
11
10
  from ormlambda.sql.clause_info import IAggregate
12
11
  from ormlambda.sql.types import TupleJoinType, ColumnType
13
- from ormlambda.databases.my_sql.join_context import JoinContext
12
+ from ormlambda.components.join import JoinContext
14
13
  from ormlambda.common.enums import JoinType
15
14
  from ormlambda.sql.clause_info import ClauseInfo
16
15
  from ormlambda.sql.types import AliasType
@@ -249,33 +248,33 @@ class IStatements[T: Table](ABC):
249
248
  # @overload
250
249
  # def select[TFlavour](self, selector: Optional[Callable[[T], tuple]] = ..., *, cast_to_tuple: bool = ..., flavour: Type[TFlavour], by: Optional[Enum] = ..., **kwargs) -> TFlavour: ...
251
250
  @overload
252
- def select[TRes](self, selector: SelectorFlavourType[T, TRes] = ..., *, flavour: Type[tuple], by: Optional[Enum] = ..., **kwargs) -> tuple[TRes, ...]: ...
251
+ def select[TRes](self, selector: SelectorFlavourType[T, TRes] = ..., *, flavour: Type[tuple], by: Optional[Enum] = ..., alias: Optional[AliasType[ClauseInfo[T]]] = ..., **kwargs) -> tuple[TRes, ...]: ...
253
252
  @overload
254
- def select[*TRes](self, selector: SelectorFlavourType[T, tuple[*TRes]] = ..., *, flavour: Type[tuple], by: Optional[Enum] = ..., **kwargs) -> tuple[tuple[*TRes]]: ...
253
+ def select[*TRes](self, selector: SelectorFlavourType[T, tuple[*TRes]] = ..., *, flavour: Type[tuple], by: Optional[Enum] = ..., alias: Optional[AliasType[ClauseInfo[T]]] = ..., **kwargs) -> tuple[tuple[*TRes]]: ...
255
254
  @overload
256
- def select[TFlavour](self, selector: SelectorFlavourType[T, tuple] = ..., *, flavour: Type[TFlavour], by: Optional[Enum] = ..., **kwargs) -> tuple[TFlavour, ...]: ...
255
+ def select[TFlavour](self, selector: SelectorFlavourType[T, tuple] = ..., *, flavour: Type[TFlavour], by: Optional[Enum] = ..., alias: Optional[AliasType[ClauseInfo[T]]] = ..., **kwargs) -> tuple[TFlavour, ...]: ...
257
256
 
258
257
  @abstractmethod
259
- def select[TValue, TFlavour, P](self, selector: SelectorFlavourType[T, tuple[TValue, P]] = ..., *, cast_to_tuple: bool = ..., flavour: Type[TFlavour] = ..., by: JoinType = ..., **kwargs): ...
258
+ def select[TValue, TFlavour, P](self, selector: SelectorFlavourType[T, tuple[TValue, P]] = ..., *, cast_to_tuple: bool = ..., flavour: Type[TFlavour] = ..., by: JoinType = ..., alias: Optional[AliasType[ClauseInfo[T]]] = ..., **kwargs): ...
260
259
 
261
260
  # endregion
262
261
  # region select_one
263
262
  @overload
264
263
  def select_one(self) -> T: ...
265
264
  @overload
266
- def select_one[TFlavour](self, *, by: Optional[Enum] = ..., flavour: Type[TFlavour], **kwargs) -> TFlavour: ...
265
+ def select_one[TFlavour](self, *, by: Optional[Enum] = ..., flavour: Type[TFlavour], alias: Optional[AliasType[ClauseInfo[T]]] = ..., **kwargs) -> TFlavour: ...
267
266
  @overload
268
267
  def select_one[T1](self, selector: SelectorOneType[T, T1 | tuple[T1]], *, by: Optional[Enum] = ...) -> T1: ...
269
268
  @overload
270
269
  def select_one[*TRes](self, selector: SelectorOneType[T, tuple[*TRes]], *, by: Optional[Enum] = ...) -> tuple[*TRes]: ...
271
270
  @overload
272
- def select_one[T1](self, selector: SelectorOneType[T, tuple[T1]], *, by: Optional[Enum] = ..., flavour: Type, **kwargs) -> T1: ...
271
+ def select_one[T1](self, selector: SelectorOneType[T, tuple[T1]], *, by: Optional[Enum] = ..., flavour: Type, alias: Optional[AliasType[ClauseInfo[T]]] = ..., **kwargs) -> T1: ...
273
272
  @overload
274
- def select_one[T1, TFlavour](self, selector: SelectorOneType[T, T1], *, by: Optional[Enum] = ..., flavour: Type[TFlavour], **kwargs) -> TFlavour: ...
273
+ def select_one[T1, TFlavour](self, selector: SelectorOneType[T, T1], *, by: Optional[Enum] = ..., flavour: Type[TFlavour], alias: Optional[AliasType[ClauseInfo[T]]] = ..., **kwargs) -> TFlavour: ...
275
274
  @overload
276
- def select_one[*TRest](self, selector: SelectorOneType[T, tuple[*TRest]], *, by: Optional[Enum] = ..., flavour: Type[tuple], **kwargs) -> tuple[*TRest]: ...
275
+ def select_one[*TRest](self, selector: SelectorOneType[T, tuple[*TRest]], *, by: Optional[Enum] = ..., flavour: Type[tuple], alias: Optional[AliasType[ClauseInfo[T]]] = ..., **kwargs) -> tuple[*TRest]: ...
277
276
  @overload
278
- def select_one[TFlavour](self, selector: SelectorOneType[T, tuple], *, by: Optional[Enum] = ..., flavour: Type[TFlavour], **kwargs) -> TFlavour: ...
277
+ def select_one[TFlavour](self, selector: SelectorOneType[T, tuple], *, by: Optional[Enum] = ..., flavour: Type[TFlavour], alias: Optional[AliasType[ClauseInfo[T]]] = ..., **kwargs) -> TFlavour: ...
279
278
  @abstractmethod
280
279
  def select_one[TValue, TFlavour, *TRest](
281
280
  self,
@@ -295,15 +294,15 @@ class IStatements[T: Table](ABC):
295
294
  @overload
296
295
  def first[*TRes](self, selector: SelectorOneType[T, tuple[*TRes]], *, by: Optional[Enum] = ...) -> tuple[*TRes]: ...
297
296
  @overload
298
- def first[TFlavour](self, *, by: Optional[Enum] = ..., flavour: Type[TFlavour], **kwargs) -> TFlavour: ...
297
+ def first[TFlavour](self, *, by: Optional[Enum] = ..., flavour: Type[TFlavour], alias: Optional[AliasType[ClauseInfo[T]]] = ..., **kwargs) -> TFlavour: ...
299
298
  @overload
300
- def first[T1](self, selector: SelectorOneType[T, tuple[T1]], *, by: Optional[Enum] = ..., flavour: Type, **kwargs) -> T1: ...
299
+ def first[T1](self, selector: SelectorOneType[T, tuple[T1]], *, by: Optional[Enum] = ..., flavour: Type, alias: Optional[AliasType[ClauseInfo[T]]] = ..., **kwargs) -> T1: ...
301
300
  @overload
302
- def first[T1, TFlavour](self, selector: SelectorOneType[T, T1], *, by: Optional[Enum] = ..., flavour: Type[TFlavour], **kwargs) -> TFlavour: ...
301
+ def first[T1, TFlavour](self, selector: SelectorOneType[T, T1], *, by: Optional[Enum] = ..., flavour: Type[TFlavour], alias: Optional[AliasType[ClauseInfo[T]]] = ..., **kwargs) -> TFlavour: ...
303
302
  @overload
304
- def first[*TRest](self, selector: SelectorOneType[T, tuple[*TRest]], *, by: Optional[Enum] = ..., flavour: Type[tuple], **kwargs) -> tuple[*TRest]: ...
303
+ def first[*TRest](self, selector: SelectorOneType[T, tuple[*TRest]], *, by: Optional[Enum] = ..., flavour: Type[tuple], alias: Optional[AliasType[ClauseInfo[T]]] = ..., **kwargs) -> tuple[*TRest]: ...
305
304
  @overload
306
- def first[TFlavour](self, selector: SelectorOneType[T, tuple], *, by: Optional[Enum] = ..., flavour: Type[TFlavour], **kwargs) -> TFlavour: ...
305
+ def first[TFlavour](self, selector: SelectorOneType[T, tuple], *, by: Optional[Enum] = ..., flavour: Type[TFlavour], alias: Optional[AliasType[ClauseInfo[T]]] = ..., **kwargs) -> TFlavour: ...
307
306
  @abstractmethod
308
307
  def first[TValue, TFlavour, *TRest](
309
308
  self,
@@ -316,13 +315,17 @@ class IStatements[T: Table](ABC):
316
315
  # endregion
317
316
 
318
317
  # region groupby
318
+ @overload
319
+ def groupby[TRepo](self, column: list[SelectCols[T, TRepo]]) -> IStatements[T]: ...
320
+ @overload
321
+ def groupby[TRepo](self, column: SelectCols[T, TRepo]) -> IStatements[T]: ...
319
322
  @abstractmethod
320
- def groupby[TRepo](self, column: Callable[[T], TRepo]) -> IStatements[T]: ...
323
+ def groupby[TRepo](self, column: list[SelectCols[T, TRepo]] | SelectCols[T, TRepo]) -> IStatements[T]: ...
321
324
 
322
325
  # endregion
323
326
 
324
327
  @abstractmethod
325
- def alias[TProp](self, column: ColumnType[TProp], alias: AliasType[ClauseInfo[T]]) -> ClauseInfo[T]: ...
328
+ def alias[TProp](self, column: SelectCols[T, TProp], alias: AliasType[ClauseInfo[T]]) -> ClauseInfo[T]: ...
326
329
 
327
330
 
328
331
  class IStatements_two_generic[T, TPool](IStatements[T]):
@@ -2,7 +2,8 @@ from __future__ import annotations
2
2
  import sys
3
3
  from pathlib import Path
4
4
  from typing import Optional, Type, TYPE_CHECKING
5
- from collections import defaultdict
5
+
6
+ # from collections import defaultdict
6
7
  import importlib.util
7
8
  import inspect
8
9
  import re
@@ -12,7 +13,7 @@ if TYPE_CHECKING:
12
13
 
13
14
 
14
15
  # from ormlambda import ForeignKey
15
- from .dfs_traversal import DFSTraversal
16
+ # from .dfs_traversal import DFSTraversal
16
17
 
17
18
 
18
19
  class Node: