ormlambda 1.2.2__py3-none-any.whl → 1.3.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ormlambda/__init__.py +1 -0
- ormlambda/common/abstract_classes/abstract_model.py +7 -239
- ormlambda/common/interfaces/IRepositoryBase.py +1 -1
- ormlambda/databases/my_sql/repository.py +1 -1
- ormlambda/databases/my_sql/statements.py +176 -44
- ormlambda/model_base.py +2 -2
- {ormlambda-1.2.2.dist-info → ormlambda-1.3.0.dist-info}/METADATA +1 -1
- {ormlambda-1.2.2.dist-info → ormlambda-1.3.0.dist-info}/RECORD +10 -10
- {ormlambda-1.2.2.dist-info → ormlambda-1.3.0.dist-info}/LICENSE +0 -0
- {ormlambda-1.2.2.dist-info → ormlambda-1.3.0.dist-info}/WHEEL +0 -0
ormlambda/__init__.py
CHANGED
@@ -4,6 +4,7 @@ from .common.enums import ( # noqa: F401
|
|
4
4
|
ConditionType,
|
5
5
|
)
|
6
6
|
|
7
|
+
from .common.abstract_classes import AbstractSQLStatements # noqa: F401
|
7
8
|
from .common.interfaces import IRepositoryBase # noqa: F401
|
8
9
|
from .utils import Table, Column, ForeignKey # noqa: F401
|
9
10
|
from .utils.lambda_disassembler import Disassembler, nameof # noqa: F401
|
@@ -1,37 +1,14 @@
|
|
1
1
|
from __future__ import annotations
|
2
|
-
from typing import Any,
|
3
|
-
from enum import Enum
|
2
|
+
from typing import Any, Type, override, Iterable, Literal, TYPE_CHECKING
|
4
3
|
from collections import defaultdict
|
5
|
-
from abc import abstractmethod
|
6
|
-
import inspect
|
7
4
|
|
8
|
-
from ormlambda.utils import ForeignKey, Table
|
9
5
|
|
6
|
+
from ormlambda.utils import Table
|
10
7
|
from ormlambda.common.interfaces import IQuery, IRepositoryBase, IStatements_two_generic
|
11
8
|
|
12
9
|
if TYPE_CHECKING:
|
13
|
-
from ormlambda.common.interfaces.IStatements import OrderType
|
14
|
-
|
15
|
-
from ormlambda.components.update import UpdateQueryBase
|
16
10
|
from ormlambda.components.select import ISelect
|
17
|
-
from ormlambda.components.delete import DeleteQueryBase
|
18
|
-
from ormlambda.components.upsert import UpsertQueryBase
|
19
11
|
from ormlambda.components.select import TableColumn
|
20
|
-
from ormlambda.components.insert import InsertQueryBase
|
21
|
-
from ormlambda.components.where.abstract_where import AbstractWhere
|
22
|
-
|
23
|
-
from ormlambda.databases.my_sql.clauses.select import SelectQuery
|
24
|
-
from ormlambda.databases.my_sql.clauses.count import CountQuery
|
25
|
-
|
26
|
-
|
27
|
-
class JoinType(Enum):
|
28
|
-
RIGHT_INCLUSIVE = "RIGHT JOIN"
|
29
|
-
LEFT_INCLUSIVE = "LEFT JOIN"
|
30
|
-
RIGHT_EXCLUSIVE = "RIGHT JOIN"
|
31
|
-
LEFT_EXCLUSIVE = "LEFT JOIN"
|
32
|
-
FULL_OUTER_INCLUSIVE = "RIGHT JOIN"
|
33
|
-
FULL_OUTER_EXCLUSIVE = "RIGHT JOIN"
|
34
|
-
INNER_JOIN = "INNER JOIN"
|
35
12
|
|
36
13
|
|
37
14
|
ORDER_QUERIES = Literal["select", "join", "where", "order", "with", "group by", "limit", "offset"]
|
@@ -42,7 +19,7 @@ class AbstractSQLStatements[T: Table, TRepo](IStatements_two_generic[T, TRepo]):
|
|
42
19
|
__order__: tuple[str, ...] = ("select", "join", "where", "order", "with", "group by", "limit", "offset")
|
43
20
|
|
44
21
|
def __init__(self, model: T, repository: IRepositoryBase[TRepo]) -> None:
|
45
|
-
self.
|
22
|
+
self.__valid_repository(repository)
|
46
23
|
|
47
24
|
self._model: T = model
|
48
25
|
self._repository: IRepositoryBase[TRepo] = repository
|
@@ -54,160 +31,17 @@ class AbstractSQLStatements[T: Table, TRepo](IStatements_two_generic[T, TRepo]):
|
|
54
31
|
raise Exception(f"'{model}' class does not inherit from Table class")
|
55
32
|
|
56
33
|
@staticmethod
|
57
|
-
def
|
34
|
+
def __valid_repository(repository: Any) -> bool:
|
58
35
|
if not isinstance(repository, IRepositoryBase):
|
59
36
|
raise ValueError(f"'repository' attribute does not instance of '{IRepositoryBase.__name__}'")
|
60
37
|
return True
|
61
38
|
|
62
|
-
|
63
|
-
|
64
|
-
def INSERT_QUERY(self) -> Type[InsertQueryBase[T, TRepo]]: ...
|
65
|
-
@property
|
66
|
-
@abstractmethod
|
67
|
-
def UPSERT_QUERY(self) -> Type[UpsertQueryBase[T, TRepo]]: ...
|
68
|
-
@property
|
69
|
-
@abstractmethod
|
70
|
-
def UPDATE_QUERY(self) -> Type[UpdateQueryBase[T, TRepo]]: ...
|
71
|
-
@property
|
72
|
-
@abstractmethod
|
73
|
-
def DELETE_QUERY(self) -> Type[DeleteQueryBase[T, TRepo]]: ...
|
74
|
-
@property
|
75
|
-
@abstractmethod
|
76
|
-
def LIMIT_QUERY(self) -> Type[IQuery]: ...
|
77
|
-
@property
|
78
|
-
@abstractmethod
|
79
|
-
def OFFSET_QUERY(self) -> Type[IQuery]: ...
|
80
|
-
@property
|
81
|
-
@abstractmethod
|
82
|
-
def JOIN_QUERY(self) -> Type[IQuery]: ...
|
83
|
-
@property
|
84
|
-
@abstractmethod
|
85
|
-
def WHERE_QUERY(self) -> Type[IQuery]: ...
|
86
|
-
@property
|
87
|
-
@abstractmethod
|
88
|
-
def ORDER_QUERY(self) -> Type[IQuery]: ...
|
89
|
-
@property
|
90
|
-
@abstractmethod
|
91
|
-
def SELECT_QUERY(self) -> Type[SelectQuery]: ...
|
39
|
+
def __repr__(self):
|
40
|
+
return f"<Model: {self.__class__.__name__}>"
|
92
41
|
|
93
42
|
@property
|
94
|
-
@abstractmethod
|
95
|
-
def COUNT(self) -> Type[CountQuery]: ...
|
96
|
-
|
97
|
-
@override
|
98
|
-
def create_table(self) -> None:
|
99
|
-
if not self._repository.table_exists(self._model.__table_name__):
|
100
|
-
self._repository.execute(self._model.create_table_query())
|
101
|
-
return None
|
102
|
-
|
103
|
-
@override
|
104
|
-
def table_exists(self) -> bool:
|
105
|
-
return self._repository.table_exists(self._model.__table_name__)
|
106
|
-
|
107
|
-
@override
|
108
|
-
def insert(self, instances: T | list[T]) -> None:
|
109
|
-
insert = self.INSERT_QUERY(self._model, self._repository)
|
110
|
-
insert.insert(instances)
|
111
|
-
insert.execute()
|
112
|
-
self._query_list.clear()
|
113
|
-
return None
|
114
|
-
|
115
|
-
@override
|
116
|
-
def delete(self, instances: Optional[T | list[T]] = None) -> None:
|
117
|
-
if instances is None:
|
118
|
-
response = self.select()
|
119
|
-
if len(response) == 0:
|
120
|
-
return None
|
121
|
-
# [0] because if we do not select anything, we retrieve all columns of the unic model, stored in tuple[tuple[model]] structure.
|
122
|
-
# We always going to have a tuple of one element
|
123
|
-
return self.delete(response)
|
124
|
-
|
125
|
-
delete = self.DELETE_QUERY(self._model, self._repository)
|
126
|
-
delete.delete(instances)
|
127
|
-
delete.execute()
|
128
|
-
# not necessary to call self._query_list.clear() because select() method already call it
|
129
|
-
return None
|
130
|
-
|
131
|
-
@override
|
132
|
-
def upsert(self, instances: T | list[T]) -> None:
|
133
|
-
upsert = self.UPSERT_QUERY(self._model, self._repository)
|
134
|
-
upsert.upsert(instances)
|
135
|
-
upsert.execute()
|
136
|
-
self._query_list.clear()
|
137
|
-
return None
|
138
|
-
|
139
|
-
@override
|
140
|
-
def update(self, dicc: dict[str, Any] | list[dict[str, Any]]) -> None:
|
141
|
-
update = self.UPDATE_QUERY(self._model, self._repository, self._query_list["where"])
|
142
|
-
update.update(dicc)
|
143
|
-
update.execute()
|
144
|
-
self._query_list.clear()
|
145
|
-
return None
|
146
|
-
|
147
43
|
@override
|
148
|
-
def
|
149
|
-
limit = self.LIMIT_QUERY(number)
|
150
|
-
# Only can be one LIMIT SQL parameter. We only use the last LimitQuery
|
151
|
-
limit_list = self._query_list["limit"]
|
152
|
-
if len(limit_list) > 0:
|
153
|
-
self._query_list["limit"] = [limit]
|
154
|
-
else:
|
155
|
-
self._query_list["limit"].append(limit)
|
156
|
-
return self
|
157
|
-
|
158
|
-
@override
|
159
|
-
def offset(self, number: int) -> "IStatements_two_generic[T,TRepo]":
|
160
|
-
offset = self.OFFSET_QUERY(number)
|
161
|
-
self._query_list["offset"].append(offset)
|
162
|
-
return self
|
163
|
-
|
164
|
-
@override
|
165
|
-
def count(self) -> int:
|
166
|
-
count_select: IQuery = self.COUNT(self._model)
|
167
|
-
self._query_list["select"].append(count_select)
|
168
|
-
query = self.build()
|
169
|
-
return self.repository.read_sql(query)[0][0]
|
170
|
-
|
171
|
-
@override
|
172
|
-
def join(self, table_left: Table, table_right: Table, *, by: str) -> "IStatements_two_generic[T,TRepo]":
|
173
|
-
where = ForeignKey.MAPPED[table_left.__table_name__][table_right.__table_name__]
|
174
|
-
join_query = self.JOIN_QUERY[table_left, Table](table_left, table_right, JoinType(by), where=where)
|
175
|
-
self._query_list["join"].append(join_query)
|
176
|
-
return self
|
177
|
-
|
178
|
-
@override
|
179
|
-
def where(self, lambda_: Callable[[T], bool] = lambda: None, **kwargs) -> "IStatements_two_generic[T,TRepo]":
|
180
|
-
# FIXME [x]: I've wrapped self._model into tuple to pass it instance attr. Idk if it's correct
|
181
|
-
where_query = self.WHERE_QUERY[T](function=lambda_, instances=(self._model,), **kwargs)
|
182
|
-
self._query_list["where"].append(where_query)
|
183
|
-
return self
|
184
|
-
|
185
|
-
@override
|
186
|
-
def order[TValue](self, _lambda_col: Callable[[T], TValue], order_type: OrderType) -> "IStatements_two_generic[T,TRepo]":
|
187
|
-
order = self.ORDER_QUERY[T](self._model, _lambda_col, order_type)
|
188
|
-
self._query_list["order"].append(order)
|
189
|
-
return self
|
190
|
-
|
191
|
-
@override
|
192
|
-
def select[TValue, TFlavour, *Ts](self, selector: Optional[Callable[[T], tuple[TValue, *Ts]]] = lambda: None, *, flavour: Optional[Type[TFlavour]] = None, by: JoinType = JoinType.INNER_JOIN):
|
193
|
-
if len(inspect.signature(selector).parameters) == 0:
|
194
|
-
# COMMENT: if we do not specify any lambda function we assumed the user want to retreive only elements of the Model itself avoiding other models
|
195
|
-
result = self.select(selector=lambda x: (x,), flavour=flavour, by=by)
|
196
|
-
# COMMENT: Always we want to retrieve tuple[tuple[Any]]. That's the reason to return result[0] when we ensure the user want only objects of the first table.
|
197
|
-
# Otherwise, we wil return the result itself
|
198
|
-
if flavour:
|
199
|
-
return result
|
200
|
-
return () if not result else result[0]
|
201
|
-
select: ISelect = self.SELECT_QUERY(self._model, select_lambda=selector, by=by)
|
202
|
-
self._query_list["select"].append(select)
|
203
|
-
|
204
|
-
query: str = self.build()
|
205
|
-
if flavour:
|
206
|
-
result = self._return_flavour(query, flavour)
|
207
|
-
if issubclass(flavour, tuple) and isinstance(selector(self._model), property):
|
208
|
-
return tuple([x[0] for x in result])
|
209
|
-
return result
|
210
|
-
return self._return_model(select, query)
|
44
|
+
def repository(self) -> IRepositoryBase[TRepo]: ...
|
211
45
|
|
212
46
|
def _return_flavour[TValue](self, query, flavour: Type[TValue]) -> tuple[TValue]:
|
213
47
|
return self._repository.read_sql(query, flavour=flavour)
|
@@ -220,72 +54,6 @@ class AbstractSQLStatements[T: Table, TRepo](IStatements_two_generic[T, TRepo]):
|
|
220
54
|
|
221
55
|
return response_sql
|
222
56
|
|
223
|
-
@override
|
224
|
-
def select_one[TValue, TFlavour, *Ts](self, selector: Optional[Callable[[T], tuple[TValue, *Ts]]] = lambda: None, *, flavour: Optional[Type[TFlavour]] = None, by: JoinType = JoinType.INNER_JOIN):
|
225
|
-
self.limit(1)
|
226
|
-
if len(inspect.signature(selector).parameters) == 0:
|
227
|
-
response = self.select(selector=lambda x: (x,), flavour=flavour, by=by)
|
228
|
-
else:
|
229
|
-
response = self.select(selector=selector, flavour=flavour, by=by)
|
230
|
-
|
231
|
-
if flavour:
|
232
|
-
return response[0] if response else None
|
233
|
-
|
234
|
-
# response var could be return more than one element when we work with models an we
|
235
|
-
# select columns from different tables using a join query
|
236
|
-
if len(response) == 1 and len(response[0]) == 1:
|
237
|
-
return response[0][0]
|
238
|
-
return tuple([res[0] for res in response])
|
239
|
-
|
240
|
-
@override
|
241
|
-
def build(self) -> str:
|
242
|
-
query: str = ""
|
243
|
-
|
244
|
-
self._create_necessary_inner_join()
|
245
|
-
for x in self.__order__:
|
246
|
-
if sub_query := self._query_list.get(x, None):
|
247
|
-
if isinstance(sub_query[0], self.WHERE_QUERY):
|
248
|
-
query_ = self.__build_where_clause(sub_query)
|
249
|
-
|
250
|
-
# we must check if any join already exists on query string
|
251
|
-
elif isinstance(sub_query[0], self.JOIN_QUERY):
|
252
|
-
select_query: str = self._query_list["select"][0].query
|
253
|
-
query_ = ""
|
254
|
-
for join in sub_query:
|
255
|
-
if join.query not in select_query:
|
256
|
-
query_ += f"\n{join.query}"
|
257
|
-
else:
|
258
|
-
query_ = "\n".join([x.query for x in sub_query])
|
259
|
-
|
260
|
-
query += f"\n{query_}" if query != "" else query_
|
261
|
-
self._query_list.clear()
|
262
|
-
return query
|
263
|
-
|
264
|
-
def __build_where_clause(self, where_condition: list[AbstractWhere]) -> str:
|
265
|
-
query: str = where_condition[0].query
|
266
|
-
|
267
|
-
for where in where_condition[1:]:
|
268
|
-
q = where.query.replace(where.WHERE, "AND")
|
269
|
-
and_, clause = q.split(" ", maxsplit=1)
|
270
|
-
query += f" {and_} ({clause})"
|
271
|
-
return query
|
272
|
-
|
273
|
-
def _create_necessary_inner_join(self) -> None:
|
274
|
-
# 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.
|
275
|
-
if "where" not in self._query_list:
|
276
|
-
return None
|
277
|
-
|
278
|
-
where: AbstractWhere = self._query_list["where"][0]
|
279
|
-
involved_tables = where.get_involved_tables()
|
280
|
-
|
281
|
-
select: ISelect = self._query_list["select"][0]
|
282
|
-
if not involved_tables or (set(involved_tables) == set(select.tables_heritage)):
|
283
|
-
return None
|
284
|
-
|
285
|
-
for l_tbl, r_tbl in involved_tables:
|
286
|
-
# FIXME [ ]: Checked what function was called by the self.join method before the change
|
287
|
-
self.join(l_tbl, r_tbl, by="INNER JOIN")
|
288
|
-
|
289
57
|
|
290
58
|
class ClusterQuery:
|
291
59
|
def __init__(self, select: ISelect, response_sql: tuple[dict[str, Any]]) -> None:
|
@@ -25,7 +25,7 @@ class IRepositoryBase[T](ABC):
|
|
25
25
|
def is_connected(self) -> bool: ...
|
26
26
|
|
27
27
|
@abstractmethod
|
28
|
-
def connect(self, **kwargs: Any) ->
|
28
|
+
def connect(self, **kwargs: Any) -> None: ...
|
29
29
|
|
30
30
|
@abstractmethod
|
31
31
|
def close_connection(self) -> None: ...
|
@@ -85,7 +85,7 @@ class MySQLRepository(IRepositoryBase[MySQLConnection]):
|
|
85
85
|
return self._connection.is_connected()
|
86
86
|
|
87
87
|
@override
|
88
|
-
def connect(self) ->
|
88
|
+
def connect(self) -> None:
|
89
89
|
# return MySQLConnectionPool(pool_name="mypool", pool_size=5, **kwargs)
|
90
90
|
self._connection.connect(**self._data_config)
|
91
91
|
return None
|
@@ -1,14 +1,18 @@
|
|
1
1
|
from __future__ import annotations
|
2
|
-
from typing import override, Type, TYPE_CHECKING
|
2
|
+
from typing import override, Type, TYPE_CHECKING, Any, Callable, Optional
|
3
3
|
|
4
4
|
if TYPE_CHECKING:
|
5
5
|
from ormlambda import Table
|
6
6
|
from ormlambda.components.select import ISelect
|
7
|
+
from ormlambda.components.where.abstract_where import AbstractWhere
|
8
|
+
from ormlambda.common.interfaces.IStatements import OrderType
|
9
|
+
from ormlambda.common.interfaces import IQuery, IRepositoryBase, IStatements_two_generic
|
10
|
+
|
11
|
+
from ormlambda.databases.my_sql.clauses.select import SelectQuery
|
12
|
+
from ormlambda.databases.my_sql.clauses.count import CountQuery
|
7
13
|
|
8
|
-
from ormlambda.common.abstract_classes import AbstractSQLStatements
|
9
|
-
|
10
|
-
from ormlambda.common.interfaces import IQuery, IRepositoryBase
|
11
14
|
|
15
|
+
from ormlambda import AbstractSQLStatements
|
12
16
|
from .clauses import DeleteQuery
|
13
17
|
from .clauses import InsertQuery
|
14
18
|
from .clauses import JoinSelector
|
@@ -24,72 +28,200 @@ from .clauses import CountQuery
|
|
24
28
|
from mysql.connector import MySQLConnection
|
25
29
|
|
26
30
|
|
31
|
+
|
32
|
+
import inspect
|
33
|
+
|
34
|
+
from ormlambda.utils import ForeignKey, Table
|
35
|
+
from ormlambda.common.enums import JoinType
|
36
|
+
|
37
|
+
|
38
|
+
|
27
39
|
class MySQLStatements[T: Table](AbstractSQLStatements[T, MySQLConnection]):
|
28
40
|
def __init__(self, model: T, repository: IRepositoryBase[MySQLConnection]) -> None:
|
29
41
|
super().__init__(model, repository=repository)
|
30
42
|
|
31
|
-
|
32
|
-
|
33
|
-
|
43
|
+
@property
|
44
|
+
@override
|
45
|
+
def repository(self) -> IRepositoryBase[MySQLConnection]:
|
46
|
+
return self._repository
|
34
47
|
|
35
|
-
|
48
|
+
@override
|
49
|
+
def create_table(self) -> None:
|
50
|
+
if not self._repository.table_exists(self._model.__table_name__):
|
51
|
+
self._repository.execute(self._model.create_table_query())
|
52
|
+
return None
|
36
53
|
|
37
|
-
@property
|
38
54
|
@override
|
39
|
-
def
|
40
|
-
return
|
55
|
+
def table_exists(self) -> bool:
|
56
|
+
return self._repository.table_exists(self._model.__table_name__)
|
41
57
|
|
42
|
-
@property
|
43
58
|
@override
|
44
|
-
def
|
45
|
-
|
59
|
+
def insert(self, instances: T | list[T]) -> None:
|
60
|
+
insert = InsertQuery(self._model, self._repository)
|
61
|
+
insert.insert(instances)
|
62
|
+
insert.execute()
|
63
|
+
self._query_list.clear()
|
64
|
+
return None
|
46
65
|
|
47
|
-
@property
|
48
66
|
@override
|
49
|
-
def
|
50
|
-
|
67
|
+
def delete(self, instances: Optional[T | list[T]] = None) -> None:
|
68
|
+
if instances is None:
|
69
|
+
response = self.select()
|
70
|
+
if len(response) == 0:
|
71
|
+
return None
|
72
|
+
# [0] because if we do not select anything, we retrieve all columns of the unic model, stored in tuple[tuple[model]] structure.
|
73
|
+
# We always going to have a tuple of one element
|
74
|
+
return self.delete(response)
|
75
|
+
|
76
|
+
delete = DeleteQuery(self._model, self._repository)
|
77
|
+
delete.delete(instances)
|
78
|
+
delete.execute()
|
79
|
+
# not necessary to call self._query_list.clear() because select() method already call it
|
80
|
+
return None
|
51
81
|
|
52
82
|
@override
|
53
|
-
|
54
|
-
|
55
|
-
|
83
|
+
def upsert(self, instances: T | list[T]) -> None:
|
84
|
+
upsert = UpsertQuery(self._model, self._repository)
|
85
|
+
upsert.upsert(instances)
|
86
|
+
upsert.execute()
|
87
|
+
self._query_list.clear()
|
88
|
+
return None
|
56
89
|
|
57
|
-
@property
|
58
90
|
@override
|
59
|
-
def
|
60
|
-
|
91
|
+
def update(self, dicc: dict[str, Any] | list[dict[str, Any]]) -> None:
|
92
|
+
update = UpdateQuery(self._model, self._repository, self._query_list["where"])
|
93
|
+
update.update(dicc)
|
94
|
+
update.execute()
|
95
|
+
self._query_list.clear()
|
96
|
+
return None
|
61
97
|
|
62
|
-
@property
|
63
98
|
@override
|
64
|
-
def
|
65
|
-
|
99
|
+
def limit(self, number: int) -> IStatements_two_generic[T, MySQLConnection]:
|
100
|
+
limit = LimitQuery(number)
|
101
|
+
# Only can be one LIMIT SQL parameter. We only use the last LimitQuery
|
102
|
+
limit_list = self._query_list["limit"]
|
103
|
+
if len(limit_list) > 0:
|
104
|
+
self._query_list["limit"] = [limit]
|
105
|
+
else:
|
106
|
+
self._query_list["limit"].append(limit)
|
107
|
+
return self
|
66
108
|
|
67
|
-
@property
|
68
109
|
@override
|
69
|
-
def
|
70
|
-
|
110
|
+
def offset(self, number: int) -> IStatements_two_generic[T, MySQLConnection]:
|
111
|
+
offset = OffsetQuery(number)
|
112
|
+
self._query_list["offset"].append(offset)
|
113
|
+
return self
|
71
114
|
|
72
|
-
@property
|
73
115
|
@override
|
74
|
-
def
|
75
|
-
|
116
|
+
def count(self) -> int:
|
117
|
+
count_select: IQuery = CountQuery(self._model)
|
118
|
+
self._query_list["select"].append(count_select)
|
119
|
+
query = self.build()
|
120
|
+
return self.repository.read_sql(query)[0][0]
|
76
121
|
|
77
|
-
@property
|
78
122
|
@override
|
79
|
-
def
|
80
|
-
|
123
|
+
def join(self, table_left: Table, table_right: Table, *, by: str) -> IStatements_two_generic[T, MySQLConnection]:
|
124
|
+
where = ForeignKey.MAPPED[table_left.__table_name__][table_right.__table_name__]
|
125
|
+
join_query = JoinSelector[table_left, Table](table_left, table_right, JoinType(by), where=where)
|
126
|
+
self._query_list["join"].append(join_query)
|
127
|
+
return self
|
81
128
|
|
82
|
-
@property
|
83
129
|
@override
|
84
|
-
def
|
85
|
-
|
130
|
+
def where(self, lambda_: Callable[[T], bool] = lambda: None, **kwargs) -> IStatements_two_generic[T, MySQLConnection]:
|
131
|
+
# FIXME [x]: I've wrapped self._model into tuple to pass it instance attr. Idk if it's correct
|
132
|
+
where_query = WhereCondition[T](function=lambda_, instances=(self._model,), **kwargs)
|
133
|
+
self._query_list["where"].append(where_query)
|
134
|
+
return self
|
86
135
|
|
87
|
-
@property
|
88
136
|
@override
|
89
|
-
def
|
90
|
-
|
137
|
+
def order[TValue](self, _lambda_col: Callable[[T], TValue], order_type: OrderType) -> IStatements_two_generic[T, MySQLConnection]:
|
138
|
+
order = OrderQuery[T](self._model, _lambda_col, order_type)
|
139
|
+
self._query_list["order"].append(order)
|
140
|
+
return self
|
91
141
|
|
92
|
-
@property
|
93
142
|
@override
|
94
|
-
def
|
95
|
-
|
143
|
+
def select[TValue, TFlavour, *Ts](self, selector: Optional[Callable[[T], tuple[TValue, *Ts]]] = lambda: None, *, flavour: Optional[Type[TFlavour]] = None, by: JoinType = JoinType.INNER_JOIN):
|
144
|
+
if len(inspect.signature(selector).parameters) == 0:
|
145
|
+
# COMMENT: if we do not specify any lambda function we assumed the user want to retreive only elements of the Model itself avoiding other models
|
146
|
+
result = self.select(selector=lambda x: (x,), flavour=flavour, by=by)
|
147
|
+
# COMMENT: Always we want to retrieve tuple[tuple[Any]]. That's the reason to return result[0] when we ensure the user want only objects of the first table.
|
148
|
+
# Otherwise, we wil return the result itself
|
149
|
+
if flavour:
|
150
|
+
return result
|
151
|
+
return () if not result else result[0]
|
152
|
+
select: ISelect = SelectQuery(self._model, select_lambda=selector, by=by)
|
153
|
+
self._query_list["select"].append(select)
|
154
|
+
|
155
|
+
query: str = self.build()
|
156
|
+
if flavour:
|
157
|
+
result = self._return_flavour(query, flavour)
|
158
|
+
if issubclass(flavour, tuple) and isinstance(selector(self._model), property):
|
159
|
+
return tuple([x[0] for x in result])
|
160
|
+
return result
|
161
|
+
return self._return_model(select, query)
|
162
|
+
|
163
|
+
@override
|
164
|
+
def select_one[TValue, TFlavour, *Ts](self, selector: Optional[Callable[[T], tuple[TValue, *Ts]]] = lambda: None, *, flavour: Optional[Type[TFlavour]] = None, by: JoinType = JoinType.INNER_JOIN):
|
165
|
+
self.limit(1)
|
166
|
+
if len(inspect.signature(selector).parameters) == 0:
|
167
|
+
response = self.select(selector=lambda x: (x,), flavour=flavour, by=by)
|
168
|
+
else:
|
169
|
+
response = self.select(selector=selector, flavour=flavour, by=by)
|
170
|
+
|
171
|
+
if flavour:
|
172
|
+
return response[0] if response else None
|
173
|
+
|
174
|
+
# response var could be return more than one element when we work with models an we
|
175
|
+
# select columns from different tables using a join query
|
176
|
+
if len(response) == 1 and len(response[0]) == 1:
|
177
|
+
return response[0][0]
|
178
|
+
return tuple([res[0] for res in response])
|
179
|
+
|
180
|
+
@override
|
181
|
+
def build(self) -> str:
|
182
|
+
query: str = ""
|
183
|
+
|
184
|
+
self.__create_necessary_inner_join()
|
185
|
+
for x in self.__order__:
|
186
|
+
if sub_query := self._query_list.get(x, None):
|
187
|
+
if isinstance(sub_query[0], WhereCondition):
|
188
|
+
query_ = self.__build_where_clause(sub_query)
|
189
|
+
|
190
|
+
# we must check if any join already exists on query string
|
191
|
+
elif isinstance(sub_query[0], JoinSelector):
|
192
|
+
select_query: str = self._query_list["select"][0].query
|
193
|
+
query_ = ""
|
194
|
+
for join in sub_query:
|
195
|
+
if join.query not in select_query:
|
196
|
+
query_ += f"\n{join.query}"
|
197
|
+
else:
|
198
|
+
query_ = "\n".join([x.query for x in sub_query])
|
199
|
+
|
200
|
+
query += f"\n{query_}" if query != "" else query_
|
201
|
+
self._query_list.clear()
|
202
|
+
return query
|
203
|
+
|
204
|
+
def __build_where_clause(self, where_condition: list[AbstractWhere]) -> str:
|
205
|
+
query: str = where_condition[0].query
|
206
|
+
|
207
|
+
for where in where_condition[1:]:
|
208
|
+
q = where.query.replace(where.WHERE, "AND")
|
209
|
+
and_, clause = q.split(" ", maxsplit=1)
|
210
|
+
query += f" {and_} ({clause})"
|
211
|
+
return query
|
212
|
+
|
213
|
+
def __create_necessary_inner_join(self) -> None:
|
214
|
+
# 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.
|
215
|
+
if "where" not in self._query_list:
|
216
|
+
return None
|
217
|
+
|
218
|
+
where: AbstractWhere = self._query_list["where"][0]
|
219
|
+
involved_tables = where.get_involved_tables()
|
220
|
+
|
221
|
+
select: ISelect = self._query_list["select"][0]
|
222
|
+
if not involved_tables or (set(involved_tables) == set(select.tables_heritage)):
|
223
|
+
return None
|
224
|
+
|
225
|
+
for l_tbl, r_tbl in involved_tables:
|
226
|
+
# FIXME [ ]: Checked what function was called by the self.join method before the change
|
227
|
+
self.join(l_tbl, r_tbl, by="INNER JOIN")
|
ormlambda/model_base.py
CHANGED
@@ -10,7 +10,7 @@ from .databases.my_sql import MySQLStatements, MySQLRepository
|
|
10
10
|
# endregion
|
11
11
|
|
12
12
|
|
13
|
-
class BaseModel[T: Table]:
|
13
|
+
class BaseModel[T: Type[Table]]:
|
14
14
|
"""
|
15
15
|
Class to select the correct AbstractSQLStatements class depends on the repository.
|
16
16
|
|
@@ -27,7 +27,7 @@ class BaseModel[T: Table]:
|
|
27
27
|
cls: AbstractSQLStatements[T, TRepo] = cls.statements_dicc.get(type(repository), None)
|
28
28
|
|
29
29
|
if not cls:
|
30
|
-
raise Exception(f"
|
30
|
+
raise Exception(f"The selected repository '{repository}' does not exist.")
|
31
31
|
|
32
32
|
self = object().__new__(cls)
|
33
33
|
cls.__init__(self, model, repository)
|
@@ -1,7 +1,7 @@
|
|
1
|
-
ormlambda/__init__.py,sha256=
|
1
|
+
ormlambda/__init__.py,sha256=L-Enc4FPmW3ldBMnWOU3gLn9i4FEc7sQzLWy2mIEti8,538
|
2
2
|
ormlambda/common/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
3
3
|
ormlambda/common/abstract_classes/__init__.py,sha256=tk2J4Mn_nD-1ZjtMVBE5FH7KR_8ppN_1Kx1s6c38GjM,167
|
4
|
-
ormlambda/common/abstract_classes/abstract_model.py,sha256=
|
4
|
+
ormlambda/common/abstract_classes/abstract_model.py,sha256=Eguy4jQQdLAKvYZgDqUB1V4driBsYoiDuyPZ-DwPi5A,3867
|
5
5
|
ormlambda/common/abstract_classes/non_query_base.py,sha256=5jhvyT7OZaJxlGp9XMP3vQ4ei5QQZBn-fFtJnD640mE,980
|
6
6
|
ormlambda/common/abstract_classes/query_base.py,sha256=6qUFPwsVx45kUW3b66pHiSyjhcH4mzbdkddlGeUnG7c,266
|
7
7
|
ormlambda/common/enums/__init__.py,sha256=4lVKCHi1JalwgNzjsAXqX-C54NJEH83y2v5baMO8fN4,103
|
@@ -9,7 +9,7 @@ ormlambda/common/enums/condition_types.py,sha256=mDPMrtzZu4IYv3-q5Zw4NNgwUoNAx4l
|
|
9
9
|
ormlambda/common/enums/join_type.py,sha256=7LEhE34bzYOwJxe8yxPJo_EjQpGylgClAPX1Wt3z4n4,292
|
10
10
|
ormlambda/common/interfaces/INonQueryCommand.py,sha256=7CjLW4sKqkR5zUIGvhRXOtzTs6vypJW1a9EJHlgCw2c,260
|
11
11
|
ormlambda/common/interfaces/IQueryCommand.py,sha256=hfzCosK4-n8RJIb2PYs8b0qU3TNmfYluZXBf47KxxKs,331
|
12
|
-
ormlambda/common/interfaces/IRepositoryBase.py,sha256=
|
12
|
+
ormlambda/common/interfaces/IRepositoryBase.py,sha256=UAnt_ZFwfyx6vDXiWoF69rqbLySirhDlVEghMYzuDJ0,1923
|
13
13
|
ormlambda/common/interfaces/IStatements.py,sha256=tDQ7LviuNOCPCblSCvpnwSC5dyygWVpQ_TvxtJ2Jzp4,9628
|
14
14
|
ormlambda/common/interfaces/__init__.py,sha256=4tthR8pNIHGvgu0n7i3ZEQYP_R3wmqh40l34cAZbM2o,244
|
15
15
|
ormlambda/components/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -47,9 +47,9 @@ ormlambda/databases/my_sql/clauses/select.py,sha256=fUeyh2FKud1FOWUGrZCiX-Q2YAcC
|
|
47
47
|
ormlambda/databases/my_sql/clauses/update.py,sha256=3Htw0_PT3EckEiF214-V2r63lcNoRBroKZI9yg48Ddg,1867
|
48
48
|
ormlambda/databases/my_sql/clauses/upsert.py,sha256=eW2pQ4ax-GKuXiaWKoSRSS1GrHuILJBsmj83ADbBQ34,1951
|
49
49
|
ormlambda/databases/my_sql/clauses/where_condition.py,sha256=eG514q4Boc2HCZl9MlI5vBKhwQHS9ANpHcTFgLw85vw,8314
|
50
|
-
ormlambda/databases/my_sql/repository.py,sha256=
|
51
|
-
ormlambda/databases/my_sql/statements.py,sha256=
|
52
|
-
ormlambda/model_base.py,sha256=
|
50
|
+
ormlambda/databases/my_sql/repository.py,sha256=xKbtitr2R7V89La_hL1TE-sexfQqw-s5N51SIuCXH7I,7163
|
51
|
+
ormlambda/databases/my_sql/statements.py,sha256=3WKlQrxSksceJQXeGh16DRu-Upr_lsY9JZpt4bB2kwk,9452
|
52
|
+
ormlambda/model_base.py,sha256=x7PUPNslRh4hTNsDDUwuh_maKjEFsHIvee4a6rcyNV8,1064
|
53
53
|
ormlambda/utils/__init__.py,sha256=ywMdWqmA2jHj19-W-S04yfaYF5hv4IZ1lZDq0B8Jnjs,142
|
54
54
|
ormlambda/utils/column.py,sha256=5FAGzCU4yvNS4MhwJJ5i73h7RvHD5UCVox0NdzMsMiE,1945
|
55
55
|
ormlambda/utils/dtypes.py,sha256=1kRsT5JggNS1DMWgSUdBb67CefQHnZ-VTN1WI7nN2yQ,7879
|
@@ -65,7 +65,7 @@ ormlambda/utils/module_tree/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMp
|
|
65
65
|
ormlambda/utils/module_tree/dfs_traversal.py,sha256=lSF03G63XtJFLp03ueAmsHMBvhUkjptDbK3IugXm8iU,1425
|
66
66
|
ormlambda/utils/module_tree/dynamic_module.py,sha256=zwvjU3U2cz6H2CDp9Gncs5D5bSAyfITNa2SDqFDl8rw,8551
|
67
67
|
ormlambda/utils/table_constructor.py,sha256=I2oFTfa6BPTILY6UDYGZy4fJxZazZMkH89NObkfRemo,11480
|
68
|
-
ormlambda-1.
|
69
|
-
ormlambda-1.
|
70
|
-
ormlambda-1.
|
71
|
-
ormlambda-1.
|
68
|
+
ormlambda-1.3.0.dist-info/LICENSE,sha256=xBprFw8GJLdHMOoUqDk0427EvjIcbEREvXXVFULuuXU,1080
|
69
|
+
ormlambda-1.3.0.dist-info/METADATA,sha256=npu-nY-qxCZebgn5C1oXA87apBV-z3Vnsv75OEumy3w,8428
|
70
|
+
ormlambda-1.3.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
71
|
+
ormlambda-1.3.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|