ormlambda 2.0.2__py3-none-any.whl → 2.6.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/common/abstract_classes/abstract_model.py +28 -13
- ormlambda/common/abstract_classes/decomposition_query.py +301 -0
- ormlambda/common/interfaces/IAggregate.py +10 -0
- ormlambda/common/interfaces/IDecompositionQuery.py +56 -0
- ormlambda/common/interfaces/IStatements.py +8 -2
- ormlambda/common/interfaces/__init__.py +2 -0
- ormlambda/databases/my_sql/clauses/__init__.py +16 -14
- ormlambda/databases/my_sql/clauses/count.py +27 -23
- ormlambda/databases/my_sql/clauses/select.py +28 -155
- ormlambda/databases/my_sql/clauses/where_condition.py +2 -2
- ormlambda/databases/my_sql/functions/__init__.py +3 -0
- ormlambda/databases/my_sql/functions/concat.py +41 -0
- ormlambda/databases/my_sql/functions/group_by.py +37 -0
- ormlambda/databases/my_sql/functions/max.py +39 -0
- ormlambda/databases/my_sql/repository.py +18 -15
- ormlambda/databases/my_sql/statements.py +18 -32
- ormlambda/model_base.py +2 -0
- ormlambda/utils/table_constructor.py +5 -4
- {ormlambda-2.0.2.dist-info → ormlambda-2.6.0.dist-info}/METADATA +1 -1
- {ormlambda-2.0.2.dist-info → ormlambda-2.6.0.dist-info}/RECORD +22 -18
- ormlambda/components/select/ISelect.py +0 -14
- ormlambda/components/select/__init__.py +0 -2
- ormlambda/components/select/table_column.py +0 -43
- {ormlambda-2.0.2.dist-info → ormlambda-2.6.0.dist-info}/LICENSE +0 -0
- {ormlambda-2.0.2.dist-info → ormlambda-2.6.0.dist-info}/WHEEL +0 -0
@@ -1,172 +1,45 @@
|
|
1
|
-
from typing import
|
2
|
-
import inspect
|
1
|
+
from typing import override, Type, Callable, TYPE_CHECKING
|
3
2
|
|
4
|
-
from ormlambda.
|
5
|
-
from ormlambda.
|
6
|
-
from ormlambda import Table, ForeignKey
|
7
|
-
from ormlambda.utils.table_constructor import TableMeta
|
3
|
+
from ormlambda.common.abstract_classes.decomposition_query import DecompositionQueryBase
|
4
|
+
from ormlambda.common.enums.join_type import JoinType
|
8
5
|
|
9
|
-
|
6
|
+
if TYPE_CHECKING:
|
7
|
+
from ormlambda import Table
|
10
8
|
|
11
9
|
|
12
|
-
class
|
13
|
-
|
10
|
+
class Select[T: Type[Table]](DecompositionQueryBase[T]):
|
11
|
+
CLAUSE: str = "SELECT"
|
14
12
|
|
15
13
|
def __init__(
|
16
14
|
self,
|
17
|
-
|
18
|
-
|
15
|
+
table: T,
|
16
|
+
lambda_query: Callable[[T], tuple] = lambda x: x,
|
19
17
|
*,
|
18
|
+
alias: bool = False,
|
19
|
+
alias_name: str | None = None,
|
20
20
|
by: JoinType = JoinType.INNER_JOIN,
|
21
21
|
) -> None:
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
self._select_list: list[TableColumn] = self._rename_recursive_column_list(select_lambda)
|
34
|
-
|
35
|
-
def _rename_recursive_column_list(self, _lambda: Optional[Callable[[T], None]]) -> list[TableColumn]:
|
36
|
-
"""
|
37
|
-
Recursive function tu replace variable names by Select Query
|
38
|
-
|
39
|
-
lambda a: (a.pk_address, a.city.pk_city, a.city.country.pk_country)
|
40
|
-
|
41
|
-
>>> # convert lambda expression into list of values
|
42
|
-
>>> select_list = [
|
43
|
-
>>> "a.pk_address",
|
44
|
-
>>> "a.city",
|
45
|
-
>>> "a.city.pk_city",
|
46
|
-
>>> "a.city.country",
|
47
|
-
>>> "a.city.country.pk_country",
|
48
|
-
>>> ]
|
49
|
-
>>> result = _rename_recursive_column_list(select_list)
|
50
|
-
>>> print(result)
|
51
|
-
>>> # result = [
|
52
|
-
>>> # "address.pk_address"
|
53
|
-
>>> # "city.*"
|
54
|
-
>>> # "city.pk_city"
|
55
|
-
>>> # "country.*"
|
56
|
-
>>> # "country.pk_country"
|
57
|
-
]
|
58
|
-
"""
|
59
|
-
instruction_list: list[TupleInstruction] = TreeInstruction(_lambda).to_list()
|
60
|
-
column_list: list[TableColumn] = []
|
61
|
-
|
62
|
-
for ti in instruction_list:
|
63
|
-
obj = self._lambda_var_to_table_dicc[ti.var]
|
64
|
-
|
65
|
-
var = obj.__table_name__
|
66
|
-
new_nested = ti.nested_element.parents
|
67
|
-
new_nested[0] = var
|
68
|
-
ti = TupleInstruction(var, NestedElement(new_nested))
|
69
|
-
self._get_parents(obj, ti, column_list)
|
70
|
-
return column_list
|
71
|
-
|
72
|
-
def _get_parents(self, tbl_obj: Table, tuple_inst: TupleInstruction, column_list: list[TableColumn]) -> None:
|
73
|
-
if self._user_want_all_col(tbl_obj, tuple_inst):
|
74
|
-
column_list.extend(list(TableColumn.all_columns(tbl_obj)))
|
75
|
-
return None
|
76
|
-
|
77
|
-
# if the 'last_el' var is a property, we'll know the user will want retrieve a column of the same instance of the 'tbl_obj'. Otherwise the user will want to get a column of the other instance
|
78
|
-
last_el: str = tuple_inst.nested_element.name
|
79
|
-
if self._user_want_column_of_the_same_table(tbl_obj, tuple_inst):
|
80
|
-
return column_list.append(TableColumn(tbl_obj, last_el))
|
81
|
-
|
82
|
-
parents: list[str] = tuple_inst.nested_element.parents
|
83
|
-
first_el = parents[1]
|
84
|
-
new_ti = TupleInstruction(first_el, NestedElement[str](parents[1:])) # create new TupleInstruction from the second parent to the top
|
85
|
-
new_attr = self.get_attribute_of(tbl_obj, first_el) # could be Table or property
|
86
|
-
|
87
|
-
self._add_fk_relationship(tbl_obj, new_attr)
|
88
|
-
return self._get_parents(new_attr, new_ti, column_list)
|
89
|
-
|
90
|
-
def _add_fk_relationship(self, t1: Table, t2: Table) -> None:
|
91
|
-
tuple_ = tuple([t1, t2])
|
92
|
-
if tuple_ not in self._tables_heritage:
|
93
|
-
self._tables_heritage.append(tuple_)
|
94
|
-
return None
|
95
|
-
|
96
|
-
@staticmethod
|
97
|
-
def _user_want_all_col(tbl: Table, ti: TupleInstruction) -> bool:
|
98
|
-
"""
|
99
|
-
if ti.nested_element.parents length is 1 says that the element is the table itself (table.*)
|
100
|
-
"""
|
101
|
-
return issubclass(tbl.__class__, Table | TableMeta) and len(ti.nested_element.parents) == 1
|
102
|
-
|
103
|
-
def _user_want_column_of_the_same_table(self, table: Table, ti: TupleInstruction) -> bool:
|
104
|
-
last_el: str = ti.nested_element.name
|
105
|
-
first_el = ti.nested_element.parents[1]
|
106
|
-
|
107
|
-
table_attr = self.get_attribute_of(table, first_el)
|
108
|
-
|
109
|
-
return last_el in table.__dict__ and isinstance(table_attr, property)
|
110
|
-
|
111
|
-
@staticmethod
|
112
|
-
def get_attribute_of[TProp: Table](table: TProp, _value: str) -> Optional[TProp | property]:
|
113
|
-
try:
|
114
|
-
return getattr(table.__class__, _value)
|
115
|
-
except Exception:
|
116
|
-
return getattr(table, _value, None)
|
117
|
-
|
118
|
-
def _assign_lambda_variables_to_table(self, _lambda: Callable[[T], None]) -> dict[str, Type[Table]]:
|
119
|
-
"""
|
120
|
-
return a dictionary with the lambda's parameters as keys and Type[Table] as the values
|
121
|
-
|
122
|
-
|
123
|
-
>>> res = _assign_lambda_variables_to_table(lambda a,ci,co: ...)
|
124
|
-
>>> print(res)
|
125
|
-
>>> # {
|
126
|
-
>>> # "a": Address,
|
127
|
-
>>> # "ci": City,
|
128
|
-
>>> # "co": Country,
|
129
|
-
>>> # }
|
130
|
-
"""
|
131
|
-
lambda_vars = tuple(inspect.signature(_lambda).parameters)
|
132
|
-
|
133
|
-
dicc: dict[str, Table] = {}
|
134
|
-
for i in range(len(lambda_vars)):
|
135
|
-
dicc[lambda_vars[i]] = self._tables[i]
|
136
|
-
return dicc
|
137
|
-
|
138
|
-
def _convert_select_list(self) -> str:
|
139
|
-
self._select_list = self._select_list if self._select_list else tuple(TableColumn.all_columns(self._first_table))
|
140
|
-
|
141
|
-
return ", ".join(col.column for col in self._select_list)
|
22
|
+
super().__init__(
|
23
|
+
table,
|
24
|
+
lambda_query,
|
25
|
+
alias=alias,
|
26
|
+
alias_name=alias_name,
|
27
|
+
by=by,
|
28
|
+
)
|
29
|
+
|
30
|
+
# @classmethod
|
31
|
+
# def alias_children_resolver[Tclause: Type[Table]](self, clause_info: ClauseInfo[Tclause]):
|
32
|
+
# return f"{clause.table.__table_name__}_{name}"
|
142
33
|
|
143
34
|
@override
|
144
35
|
@property
|
145
36
|
def query(self) -> str:
|
146
|
-
|
147
|
-
query: str = f"{self.
|
37
|
+
col: str = ", ".join([x.query for x in self.all_clauses])
|
38
|
+
query: str = f"{self.CLAUSE} {col} FROM {self._table.__table_name__}"
|
39
|
+
alias = ""
|
148
40
|
|
149
|
-
|
150
|
-
if
|
151
|
-
|
41
|
+
query += alias
|
42
|
+
if self.has_foreign_keys:
|
43
|
+
query += " " + self.stringify_foreign_key(" ")
|
152
44
|
|
153
|
-
sub_query: str = ""
|
154
|
-
for l_tbl, r_tbl in involved_tables:
|
155
|
-
join = JoinSelector(l_tbl, r_tbl, by=self._by, where=ForeignKey.MAPPED[l_tbl.__table_name__].referenced_tables[r_tbl.__table_name__].relationship)
|
156
|
-
sub_query += f" {join.query}"
|
157
|
-
|
158
|
-
query += sub_query
|
159
45
|
return query
|
160
|
-
|
161
|
-
@override
|
162
|
-
@property
|
163
|
-
def select_list(self) -> list[TableColumn]:
|
164
|
-
return self._select_list
|
165
|
-
|
166
|
-
@override
|
167
|
-
@property
|
168
|
-
def tables_heritage(self) -> list[tuple[Table, Table]]:
|
169
|
-
return self._tables_heritage
|
170
|
-
|
171
|
-
def get_involved_tables(self) -> tuple[tuple[Table, Table]]:
|
172
|
-
return tuple(self._tables_heritage)
|
@@ -1,4 +1,4 @@
|
|
1
|
-
from typing import Any, Callable, Optional, override
|
1
|
+
from typing import Any, Callable, Optional, override, Type
|
2
2
|
import inspect
|
3
3
|
|
4
4
|
from ormlambda.common.enums import ConditionType
|
@@ -19,7 +19,7 @@ class WhereConditionByArg[TProp1, TProp2](IQuery):
|
|
19
19
|
return f"WHERE {self.cond1} {self.symbol.value} {self.cond2}"
|
20
20
|
|
21
21
|
|
22
|
-
class WhereCondition[*Inst](AbstractWhere):
|
22
|
+
class WhereCondition[T: Type[Table], *Inst](AbstractWhere):
|
23
23
|
"""
|
24
24
|
The purpose of this class is to create 'WHERE' condition queries properly.
|
25
25
|
|
@@ -0,0 +1,41 @@
|
|
1
|
+
from ormlambda.common.interfaces.IAggregate import IAggregate
|
2
|
+
from ormlambda.common.abstract_classes.decomposition_query import DecompositionQueryBase, ClauseInfo
|
3
|
+
|
4
|
+
|
5
|
+
import typing as tp
|
6
|
+
from ormlambda.common.enums.join_type import JoinType
|
7
|
+
|
8
|
+
if tp.TYPE_CHECKING:
|
9
|
+
from ormlambda import Table
|
10
|
+
|
11
|
+
|
12
|
+
class Concat[T: tp.Type[Table]](DecompositionQueryBase[T], IAggregate[T]):
|
13
|
+
CLAUSE = "CONCAT"
|
14
|
+
|
15
|
+
def __init__[*Ts](
|
16
|
+
self,
|
17
|
+
table: T,
|
18
|
+
lambda_query: str | tp.Callable[[T], tuple[*Ts]],
|
19
|
+
*,
|
20
|
+
alias: bool = True,
|
21
|
+
alias_name: str = "CONCAT",
|
22
|
+
by: JoinType = JoinType.INNER_JOIN,
|
23
|
+
) -> None:
|
24
|
+
super().__init__(
|
25
|
+
table,
|
26
|
+
lambda_query,
|
27
|
+
alias=alias,
|
28
|
+
alias_name=alias_name,
|
29
|
+
by=by,
|
30
|
+
)
|
31
|
+
|
32
|
+
def alias_children_resolver[Tclause: tp.Type[Table]](self, clause_info: ClauseInfo[Tclause]):
|
33
|
+
if isinstance(clause_info._row_column, IAggregate):
|
34
|
+
return clause_info._row_column.alias
|
35
|
+
return None
|
36
|
+
|
37
|
+
@property
|
38
|
+
def query(self) -> str:
|
39
|
+
col: str = ", ".join([x.query for x in self.all_clauses])
|
40
|
+
|
41
|
+
return f"{self.CLAUSE}({col})"
|
@@ -0,0 +1,37 @@
|
|
1
|
+
import typing as tp
|
2
|
+
from ormlambda.common.enums.join_type import JoinType
|
3
|
+
from ormlambda.common.abstract_classes.decomposition_query import ClauseInfo, DecompositionQueryBase
|
4
|
+
from ormlambda.common.interfaces.IAggregate import IAggregate
|
5
|
+
from ormlambda import Table
|
6
|
+
|
7
|
+
|
8
|
+
class GroupBy[T: tp.Type[Table], TProp](DecompositionQueryBase[T], IAggregate[T]):
|
9
|
+
CLAUSE: str = "GROUP BY"
|
10
|
+
|
11
|
+
def __init__(
|
12
|
+
self,
|
13
|
+
table: T,
|
14
|
+
column: tp.Callable[[T], TProp],
|
15
|
+
*,
|
16
|
+
alias: bool = True,
|
17
|
+
alias_name: str | None = None,
|
18
|
+
by: JoinType = JoinType.INNER_JOIN,
|
19
|
+
) -> None:
|
20
|
+
super().__init__(
|
21
|
+
table,
|
22
|
+
lambda_query=column,
|
23
|
+
alias=alias,
|
24
|
+
alias_name=alias_name,
|
25
|
+
by=by,
|
26
|
+
)
|
27
|
+
|
28
|
+
self._column: TProp = column
|
29
|
+
|
30
|
+
def alias_children_resolver[Tclause: tp.Type[Table]](self, clause_info: ClauseInfo[Tclause]):
|
31
|
+
return None
|
32
|
+
|
33
|
+
@property
|
34
|
+
def query(self) -> str:
|
35
|
+
col: str = ", ".join([x.query for x in self.all_clauses])
|
36
|
+
|
37
|
+
return f"{self.CLAUSE} {col}"
|
@@ -0,0 +1,39 @@
|
|
1
|
+
from ormlambda.common.interfaces import IAggregate
|
2
|
+
import typing as tp
|
3
|
+
|
4
|
+
from ormlambda.common.abstract_classes.decomposition_query import DecompositionQueryBase, ClauseInfo
|
5
|
+
|
6
|
+
if tp.TYPE_CHECKING:
|
7
|
+
from ormlambda import Table
|
8
|
+
|
9
|
+
|
10
|
+
class Max[T: tp.Type[Table]](DecompositionQueryBase[T], IAggregate[T]):
|
11
|
+
NAME: str = "MAX"
|
12
|
+
|
13
|
+
@tp.overload
|
14
|
+
def __init__[T: tp.Type[Table]](self, table: T, column: tp.Callable[[T], tp.Any], *, alias: bool = True, alias_name: str = "max") -> None: ...
|
15
|
+
|
16
|
+
def __init__(
|
17
|
+
self,
|
18
|
+
table: T,
|
19
|
+
column: str | tp.Callable[[T], tuple],
|
20
|
+
*,
|
21
|
+
alias: bool = True,
|
22
|
+
alias_name: str = "max",
|
23
|
+
) -> None:
|
24
|
+
super().__init__(
|
25
|
+
table,
|
26
|
+
lambda_query=column,
|
27
|
+
alias=alias,
|
28
|
+
alias_name=alias_name,
|
29
|
+
)
|
30
|
+
|
31
|
+
def alias_children_resolver[Tclause: tp.Type[Table]](self, clause_info: ClauseInfo[Tclause]):
|
32
|
+
if isinstance(clause_info._row_column, IAggregate):
|
33
|
+
return clause_info._row_column.alias
|
34
|
+
return None
|
35
|
+
|
36
|
+
@property
|
37
|
+
def query(self) -> str:
|
38
|
+
col = ", ".join([x.query for x in self.all_clauses])
|
39
|
+
return f"{self.NAME}({col})"
|
@@ -81,8 +81,10 @@ class MySQLRepository(IRepositoryBase[MySQLConnection]):
|
|
81
81
|
self.connect()
|
82
82
|
try:
|
83
83
|
foo = func(self, *args, **kwargs)
|
84
|
-
|
84
|
+
except Exception as e:
|
85
85
|
self.connection.rollback()
|
86
|
+
raise e
|
87
|
+
finally:
|
86
88
|
self.close_connection()
|
87
89
|
return foo
|
88
90
|
|
@@ -94,7 +96,8 @@ class MySQLRepository(IRepositoryBase[MySQLConnection]):
|
|
94
96
|
self._connection: PooledMySQLConnection = None
|
95
97
|
|
96
98
|
def __create_MySQLConnectionPool(self):
|
97
|
-
return MySQLConnectionPool(pool_name="mypool",pool_size=10, **self._data_config)
|
99
|
+
return MySQLConnectionPool(pool_name="mypool", pool_size=10, **self._data_config)
|
100
|
+
|
98
101
|
@override
|
99
102
|
def is_connected(self) -> bool:
|
100
103
|
return self._connection._cnx is not None if self._connection else False
|
@@ -122,7 +125,7 @@ class MySQLRepository(IRepositoryBase[MySQLConnection]):
|
|
122
125
|
- flavour: Type[TFlavour]: Useful to return tuple of any Iterable type as dict,set,list...
|
123
126
|
"""
|
124
127
|
|
125
|
-
with self._connection.cursor(buffered=True) as cursor:
|
128
|
+
with self._connection._cnx.cursor(buffered=True) as cursor:
|
126
129
|
cursor.execute(query)
|
127
130
|
values: list[tuple] = cursor.fetchall()
|
128
131
|
columns: tuple[str] = cursor.column_names
|
@@ -145,33 +148,33 @@ class MySQLRepository(IRepositoryBase[MySQLConnection]):
|
|
145
148
|
queries_list: list[str] = module_tree.get_queries()
|
146
149
|
|
147
150
|
for query in queries_list:
|
148
|
-
with self._connection.cursor(buffered=True) as cursor:
|
151
|
+
with self._connection._cnx.cursor(buffered=True) as cursor:
|
149
152
|
cursor.execute(query)
|
150
|
-
self._connection.commit()
|
153
|
+
self._connection._cnx.commit()
|
151
154
|
return None
|
152
155
|
|
153
156
|
@override
|
154
157
|
@get_connection
|
155
158
|
def executemany_with_values(self, query: str, values) -> None:
|
156
|
-
with self._connection.cursor(buffered=True) as cursor:
|
159
|
+
with self._connection._cnx.cursor(buffered=True) as cursor:
|
157
160
|
cursor.executemany(query, values)
|
158
|
-
self._connection.commit()
|
161
|
+
self._connection._cnx.commit()
|
159
162
|
return None
|
160
163
|
|
161
164
|
@override
|
162
165
|
@get_connection
|
163
166
|
def execute_with_values(self, query: str, values) -> None:
|
164
|
-
with self._connection.cursor(buffered=True) as cursor:
|
167
|
+
with self._connection._cnx.cursor(buffered=True) as cursor:
|
165
168
|
cursor.execute(query, values)
|
166
|
-
self._connection.commit()
|
169
|
+
self._connection._cnx.commit()
|
167
170
|
return None
|
168
171
|
|
169
172
|
@override
|
170
173
|
@get_connection
|
171
174
|
def execute(self, query: str) -> None:
|
172
|
-
with self._connection.cursor(buffered=True) as cursor:
|
175
|
+
with self._connection._cnx.cursor(buffered=True) as cursor:
|
173
176
|
cursor.execute(query)
|
174
|
-
self._connection.commit()
|
177
|
+
self._connection._cnx.commit()
|
175
178
|
return None
|
176
179
|
|
177
180
|
@override
|
@@ -182,7 +185,7 @@ class MySQLRepository(IRepositoryBase[MySQLConnection]):
|
|
182
185
|
@get_connection
|
183
186
|
def database_exists(self, name: str) -> bool:
|
184
187
|
query = "SHOW DATABASES LIKE %s;"
|
185
|
-
with self._connection.cursor(buffered=True) as cursor:
|
188
|
+
with self._connection._cnx.cursor(buffered=True) as cursor:
|
186
189
|
cursor.execute(query, (name,))
|
187
190
|
res = cursor.fetchmany(1)
|
188
191
|
return len(res) > 0
|
@@ -194,11 +197,11 @@ class MySQLRepository(IRepositoryBase[MySQLConnection]):
|
|
194
197
|
@override
|
195
198
|
@get_connection
|
196
199
|
def table_exists(self, name: str) -> bool:
|
197
|
-
if not self._connection.database:
|
200
|
+
if not self._connection._cnx.database:
|
198
201
|
raise Exception("No database selected")
|
199
202
|
|
200
203
|
query = "SHOW TABLES LIKE %s;"
|
201
|
-
with self._connection.cursor(buffered=True) as cursor:
|
204
|
+
with self._connection._cnx.cursor(buffered=True) as cursor:
|
202
205
|
cursor.execute(query, (name,))
|
203
206
|
res = cursor.fetchmany(1)
|
204
207
|
return len(res) > 0
|
@@ -219,4 +222,4 @@ class MySQLRepository(IRepositoryBase[MySQLConnection]):
|
|
219
222
|
@database.setter
|
220
223
|
def database(self, value: str) -> None:
|
221
224
|
self._data_config["database"] = value
|
222
|
-
self._pool = self.__create_MySQLConnectionPool()
|
225
|
+
self._pool = self.__create_MySQLConnectionPool()
|
@@ -3,15 +3,10 @@ from typing import override, Type, TYPE_CHECKING, Any, Callable, Optional
|
|
3
3
|
|
4
4
|
if TYPE_CHECKING:
|
5
5
|
from ormlambda import Table
|
6
|
-
from ormlambda.components.select import ISelect
|
7
6
|
from ormlambda.components.where.abstract_where import AbstractWhere
|
8
7
|
from ormlambda.common.interfaces.IStatements import OrderType
|
9
8
|
from ormlambda.common.interfaces import IQuery, IRepositoryBase, IStatements_two_generic
|
10
|
-
from
|
11
|
-
|
12
|
-
from ormlambda.databases.my_sql.clauses.select import SelectQuery
|
13
|
-
from ormlambda.databases.my_sql.clauses.count import CountQuery
|
14
|
-
|
9
|
+
from ormlambda.common.interfaces.IRepositoryBase import TypeExists
|
15
10
|
|
16
11
|
from ormlambda import AbstractSQLStatements
|
17
12
|
from .clauses import DeleteQuery
|
@@ -20,11 +15,13 @@ from .clauses import JoinSelector
|
|
20
15
|
from .clauses import LimitQuery
|
21
16
|
from .clauses import OffsetQuery
|
22
17
|
from .clauses import OrderQuery
|
23
|
-
from .clauses import
|
18
|
+
from .clauses.select import Select
|
19
|
+
|
24
20
|
from .clauses import UpsertQuery
|
25
21
|
from .clauses import UpdateQuery
|
26
22
|
from .clauses import WhereCondition
|
27
|
-
from .clauses import
|
23
|
+
from .clauses import Count
|
24
|
+
from .clauses import GroupBy
|
28
25
|
|
29
26
|
from mysql.connector import MySQLConnection, errors, errorcode
|
30
27
|
|
@@ -129,10 +126,10 @@ class MySQLStatements[T: Table](AbstractSQLStatements[T, MySQLConnection]):
|
|
129
126
|
return self
|
130
127
|
|
131
128
|
@override
|
132
|
-
def count(self) -> int:
|
133
|
-
count_select: IQuery =
|
129
|
+
def count(self, selection: Callable[[T], tuple] = lambda x: "*") -> int:
|
130
|
+
count_select: IQuery = Select[T](self._model, lambda x: Count[T](self._model, selection))
|
134
131
|
self._query_list["select"].append(count_select)
|
135
|
-
query = self.
|
132
|
+
query = self._build()
|
136
133
|
return self.repository.read_sql(query)[0][0]
|
137
134
|
|
138
135
|
@override
|
@@ -165,10 +162,10 @@ class MySQLStatements[T: Table](AbstractSQLStatements[T, MySQLConnection]):
|
|
165
162
|
if flavour:
|
166
163
|
return result
|
167
164
|
return () if not result else result[0]
|
168
|
-
select
|
165
|
+
select = Select[T](self._model, lambda_query=selector, by=by, alias=False)
|
169
166
|
self._query_list["select"].append(select)
|
170
167
|
|
171
|
-
query: str = self.
|
168
|
+
query: str = self._build()
|
172
169
|
if flavour:
|
173
170
|
result = self._return_flavour(query, flavour)
|
174
171
|
if issubclass(flavour, tuple) and isinstance(selector(self._model), property):
|
@@ -194,12 +191,17 @@ class MySQLStatements[T: Table](AbstractSQLStatements[T, MySQLConnection]):
|
|
194
191
|
return tuple([res[0] for res in response])
|
195
192
|
|
196
193
|
@override
|
197
|
-
def
|
194
|
+
def group_by[TRepo, *Ts](self, column: Callable[[T], TRepo], select_query: Callable[[T], tuple[*Ts]]) -> tuple[tuple[*Ts]]:
|
195
|
+
return GroupBy[T, TRepo, tuple[*Ts]](self._model, column, select_query)
|
196
|
+
|
197
|
+
@override
|
198
|
+
def _build(self) -> str:
|
198
199
|
query: str = ""
|
199
200
|
|
200
|
-
self.__create_necessary_inner_join()
|
201
|
+
# self.__create_necessary_inner_join()
|
201
202
|
for x in self.__order__:
|
202
|
-
|
203
|
+
sub_query: Optional[list[IQuery]] = self._query_list.get(x, None)
|
204
|
+
if sub_query is not None:
|
203
205
|
if isinstance(sub_query[0], WhereCondition):
|
204
206
|
query_ = self.__build_where_clause(sub_query)
|
205
207
|
|
@@ -225,19 +227,3 @@ class MySQLStatements[T: Table](AbstractSQLStatements[T, MySQLConnection]):
|
|
225
227
|
and_, clause = q.split(" ", maxsplit=1)
|
226
228
|
query += f" {and_} ({clause})"
|
227
229
|
return query
|
228
|
-
|
229
|
-
def __create_necessary_inner_join(self) -> None:
|
230
|
-
# 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.
|
231
|
-
if "where" not in self._query_list:
|
232
|
-
return None
|
233
|
-
|
234
|
-
where: AbstractWhere = self._query_list["where"][0]
|
235
|
-
involved_tables = where.get_involved_tables()
|
236
|
-
|
237
|
-
select: ISelect = self._query_list["select"][0]
|
238
|
-
if not involved_tables or (set(involved_tables) == set(select.tables_heritage)):
|
239
|
-
return None
|
240
|
-
|
241
|
-
for l_tbl, r_tbl in involved_tables:
|
242
|
-
# FIXME [ ]: Checked what function was called by the self.join method before the change
|
243
|
-
self.join(l_tbl, r_tbl, by="INNER JOIN")
|
ormlambda/model_base.py
CHANGED
@@ -24,6 +24,8 @@ class BaseModel[T: Type[Table]]:
|
|
24
24
|
# region Constructor
|
25
25
|
|
26
26
|
def __new__[TRepo](cls, model: T, repository: IRepositoryBase[TRepo]) -> IStatements_two_generic[T, TRepo]:
|
27
|
+
if repository is None:
|
28
|
+
raise ValueError("`None` cannot be passed to the `repository` attribute when calling the `BaseModel` class")
|
27
29
|
cls: AbstractSQLStatements[T, TRepo] = cls.statements_dicc.get(type(repository), None)
|
28
30
|
|
29
31
|
if not cls:
|
@@ -1,7 +1,7 @@
|
|
1
1
|
import base64
|
2
2
|
import datetime
|
3
3
|
from decimal import Decimal
|
4
|
-
from typing import Any, Iterable, Optional, Type, dataclass_transform
|
4
|
+
from typing import Any, Iterable, Optional, Type, dataclass_transform, get_type_hints
|
5
5
|
import json
|
6
6
|
|
7
7
|
from .dtypes import get_query_clausule
|
@@ -14,9 +14,9 @@ MISSING = Column()
|
|
14
14
|
|
15
15
|
|
16
16
|
class Field:
|
17
|
-
def __init__(self, name: str, type_:
|
17
|
+
def __init__(self, name: str, type_: Type, default: object) -> None:
|
18
18
|
self.name: str = name
|
19
|
-
self.type_:
|
19
|
+
self.type_: Type = type_
|
20
20
|
self.default: Column = default
|
21
21
|
|
22
22
|
def __repr__(self) -> str:
|
@@ -54,7 +54,8 @@ def delete_special_variables(dicc: dict[str, object]) -> None:
|
|
54
54
|
|
55
55
|
|
56
56
|
def get_fields[T](cls: Type[T]) -> Iterable[Field]:
|
57
|
-
annotations
|
57
|
+
# COMMENT: Used the 'get_type_hints' method to resolve typing when 'from __future__ import annotations' is in use
|
58
|
+
annotations = {key: val for key, val in get_type_hints(cls).items() if not key.startswith("_")}
|
58
59
|
|
59
60
|
# delete_special_variables(annotations)
|
60
61
|
fields = []
|