ormlambda 2.6.0__py3-none-any.whl → 2.7.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.
@@ -76,7 +76,7 @@ class ClusterQuery[T]:
76
76
  for clause in clauses:
77
77
  if not hasattr(table, clause.column):
78
78
  agg_methods = self.get_all_aggregate_method(clauses)
79
- raise ValueError(f"You cannot use aggregation method like '{agg_methods}' to return model objects")
79
+ raise ValueError(f"You cannot use aggregation method like '{agg_methods}' to return model objects. Try specifying 'flavour' attribute as 'dict'.")
80
80
  valid_attr[clause.column] = dicc_cols[clause.alias]
81
81
 
82
82
  # COMMENT: At this point we are going to instantiate Table class with specific attributes getting directly from database
@@ -28,7 +28,6 @@ class ClauseInfo[T: tp.Type[Table]]:
28
28
  self._alias: tp.Optional[str] = self._alias_children_resolver(self)
29
29
 
30
30
  self._query: str = self.__create_value_string(self._column)
31
- pass
32
31
 
33
32
  def __repr__(self) -> str:
34
33
  return f"{ClauseInfo.__name__}: {self.query}"
@@ -31,7 +31,7 @@ class IDecompositionQuery[T: tp.Type[Table]](IQuery):
31
31
 
32
32
  @property
33
33
  @abc.abstractmethod
34
- def fk_relationship(self) -> tuple[tuple[tp.Type[Table], tp.Type[Table]]]: ...
34
+ def fk_relationship(self) -> set[tuple[tp.Type[Table], tp.Type[Table]]]: ...
35
35
 
36
36
  @property
37
37
  @abc.abstractmethod
@@ -1,5 +1,5 @@
1
1
  from abc import ABC, abstractmethod
2
- from typing import Any, Literal, Optional, Type
2
+ from typing import Literal, Optional, Type
3
3
 
4
4
  TypeExists = Literal["fail", "replace", "append"]
5
5
 
@@ -8,15 +8,6 @@ class IRepositoryBase[T](ABC):
8
8
  def __repr__(self) -> str:
9
9
  return f"{IRepositoryBase.__name__}: {self.__class__.__name__}"
10
10
 
11
- @abstractmethod
12
- def is_connected(self) -> bool: ...
13
-
14
- @abstractmethod
15
- def connect(self, **kwargs: Any) -> None: ...
16
-
17
- @abstractmethod
18
- def close_connection(self) -> None: ...
19
-
20
11
  @abstractmethod
21
12
  def read_sql[TFlavour](self, query: str, flavour: Optional[Type[TFlavour]], **kwargs) -> tuple[TFlavour]: ...
22
13
 
@@ -44,10 +35,6 @@ class IRepositoryBase[T](ABC):
44
35
  @abstractmethod
45
36
  def database_exists(self, name: str) -> bool: ...
46
37
 
47
- @property
48
- @abstractmethod
49
- def connection(self) -> T: ...
50
-
51
38
  @property
52
39
  @abstractmethod
53
40
  def database(self) -> Optional[str]: ...
@@ -1,5 +1,5 @@
1
1
  from __future__ import annotations
2
- from typing import Any, Callable, Iterable, Optional, Literal, Type, overload, TYPE_CHECKING
2
+ from typing import Any, Callable, Iterable, Optional, Literal, Type, Union, overload, TYPE_CHECKING, TypeVar
3
3
  from enum import Enum
4
4
  from abc import abstractmethod, ABC
5
5
 
@@ -8,12 +8,16 @@ from ormlambda.common.enums import JoinType
8
8
 
9
9
  if TYPE_CHECKING:
10
10
  from ormlambda import Table
11
+ from .IAggregate import IAggregate
11
12
 
12
13
  OrderType = Literal["ASC", "DESC"]
13
14
 
14
15
  # TODOH: This var is duplicated from 'src\ormlambda\databases\my_sql\clauses\create_database.py'
15
16
  TypeExists = Literal["fail", "replace", "append"]
16
17
 
18
+ T = TypeVar("T")
19
+ WhereTypes = Union[Callable[[T], bool], Iterable[Callable[[T], bool]]]
20
+
17
21
 
18
22
  class IStatements[T: Table](ABC):
19
23
  @abstractmethod
@@ -45,7 +49,6 @@ class IStatements[T: Table](ABC):
45
49
  def insert(self, values: T | list[T]) -> None: ...
46
50
 
47
51
  # endregion
48
-
49
52
  # region upsert
50
53
  @overload
51
54
  def upsert(self, values: T) -> None:
@@ -76,25 +79,21 @@ class IStatements[T: Table](ABC):
76
79
  def update(self, dicc: dict[str | property, Any]) -> None: ...
77
80
 
78
81
  # endregion
79
-
80
82
  # region limit
81
83
  @abstractmethod
82
84
  def limit(self, number: int) -> IStatements[T]: ...
83
85
 
84
86
  # endregion
85
-
86
87
  # region offset
87
88
  @abstractmethod
88
89
  def offset(self, number: int) -> IStatements[T]: ...
89
90
 
90
91
  # endregion
91
-
92
92
  # region count
93
93
  @abstractmethod
94
94
  def count(self, selection: Callable[[T], property]) -> int: ...
95
95
 
96
96
  # endregion
97
-
98
97
  # region delete
99
98
  @overload
100
99
  def delete(self) -> None: ...
@@ -108,28 +107,15 @@ class IStatements[T: Table](ABC):
108
107
  def delete(self, instance: Optional[T | list[T]] = None) -> None: ...
109
108
 
110
109
  # endregion
111
-
112
110
  # region join
113
111
  @abstractmethod
114
112
  def join(self, table_left: Table, table_right: Table, *, by: str) -> IStatements[T]: ...
115
113
 
116
114
  # endregion
117
-
118
115
  # region where
119
- @overload
120
- def where(self, lambda_: Callable[[T], bool]) -> IStatements[T]:
121
- """
122
- This method creates where clause by passing the lambda's condition
123
-
124
- EXAMPLE
125
- -
126
- mb = BaseModel()
127
- mb.where(lambda a: 10 <= a.city_id <= 100)
128
- """
129
- ...
130
116
 
131
117
  @overload
132
- def where(self, lambda_: Callable[[T], Iterable]) -> IStatements[T]:
118
+ def where(self, conditions: Iterable[Callable[[T], bool]]) -> IStatements[T]:
133
119
  """
134
120
  This method creates where clause by passing the Iterable in lambda function
135
121
  EXAMPLE
@@ -140,7 +126,7 @@ class IStatements[T: Table](ABC):
140
126
  ...
141
127
 
142
128
  @overload
143
- def where(self, lambda_: Callable[[T], bool], **kwargs) -> IStatements[T]:
129
+ def where(self, conditions: Callable[[T], bool], **kwargs) -> IStatements[T]:
144
130
  """
145
131
  PARAM
146
132
  -
@@ -157,10 +143,9 @@ class IStatements[T: Table](ABC):
157
143
  ...
158
144
 
159
145
  @abstractmethod
160
- def where(self, lambda_: Callable[[T], bool] = lambda: None, **kwargs) -> IStatements[T]: ...
146
+ def where(self, conditions: WhereTypes = lambda: None, **kwargs) -> IStatements[T]: ...
161
147
 
162
148
  # endregion
163
-
164
149
  # region order
165
150
  @overload
166
151
  def order[TValue](self, _lambda_col: Callable[[T], TValue]) -> IStatements[T]: ...
@@ -170,7 +155,18 @@ class IStatements[T: Table](ABC):
170
155
  def order[TValue](self, _lambda_col: Callable[[T], TValue], order_type: OrderType) -> IStatements[T]: ...
171
156
 
172
157
  # endregion
158
+ # region concat
159
+ @overload
160
+ def concat[*Ts](self, selector: Callable[[T], tuple[*Ts]]) -> IAggregate[T]: ...
161
+
162
+ # endregion
163
+ # region max
164
+ @overload
165
+ def max[TProp](self, column: Callable[[T], TProp], alias: bool = True, alias_name: str = "max") -> TProp: ...
166
+ @overload
167
+ def min[TProp](self, column: Callable[[T], TProp], alias: bool = True, alias_name: str = "min") -> TProp: ...
173
168
 
169
+ # endregion
174
170
  # region select
175
171
  @overload
176
172
  def select(self) -> tuple[T, ...]: ...
@@ -212,7 +208,6 @@ class IStatements[T: Table](ABC):
212
208
  def select[TValue, TFlavour, *Ts](self, selector: Optional[Callable[[T], tuple[TValue, *Ts]]] = lambda: None, *, flavour: Type[TFlavour] = None, by: JoinType = JoinType.INNER_JOIN): ...
213
209
 
214
210
  # endregion
215
-
216
211
  # region select_one
217
212
  @overload
218
213
  def select_one(self) -> T: ...
@@ -234,7 +229,6 @@ class IStatements[T: Table](ABC):
234
229
  def select_one[TValue, TFlavour, *Ts](self, selector: Optional[Callable[[T], tuple[TValue, *Ts]]] = lambda: None, *, flavour: Type[TFlavour] = None, by: Optional[Enum] = JoinType.INNER_JOIN): ...
235
230
 
236
231
  # endregion
237
-
238
232
  # region group_by
239
233
  @abstractmethod
240
234
  def group_by[TRepo, *Ts](self, column: Callable[[T], TRepo], select_query: Callable[[T], tuple[*Ts]]) -> tuple[tuple[*Ts]]: ...
@@ -1,4 +1,8 @@
1
- from typing import override, Callable
1
+ from __future__ import annotations
2
+ from typing import override, Callable, TYPE_CHECKING
3
+
4
+ if TYPE_CHECKING:
5
+ from ormlambda import Table
2
6
 
3
7
  from ormlambda.utils.lambda_disassembler.tree_instruction import TreeInstruction
4
8
  from ormlambda.common.interfaces.IQueryCommand import IQuery
@@ -8,7 +12,7 @@ ASC = "ASC"
8
12
  DESC = "DESC"
9
13
 
10
14
 
11
- class OrderQuery[T](IQuery):
15
+ class OrderQuery[T:Table](IQuery):
12
16
  ORDER = "ORDER BY"
13
17
 
14
18
  def __init__(self, instance: T, order_lambda: Callable[[T], None], order_type: OrderType) -> None:
@@ -2,6 +2,7 @@ from typing import override, Type, Callable, TYPE_CHECKING
2
2
 
3
3
  from ormlambda.common.abstract_classes.decomposition_query import DecompositionQueryBase
4
4
  from ormlambda.common.enums.join_type import JoinType
5
+ from ormlambda.common.interfaces.IAggregate import IAggregate
5
6
 
6
7
  if TYPE_CHECKING:
7
8
  from ormlambda import Table
@@ -34,7 +35,15 @@ class Select[T: Type[Table]](DecompositionQueryBase[T]):
34
35
  @override
35
36
  @property
36
37
  def query(self) -> str:
37
- col: str = ", ".join([x.query for x in self.all_clauses])
38
+ cols:list[str] = []
39
+ for x in self.all_clauses:
40
+ cols.append(x.query)
41
+
42
+ if isinstance(x._row_column,IAggregate) and x._row_column.has_foreign_keys:
43
+ self._fk_relationship.update(x._row_column.fk_relationship)
44
+
45
+
46
+ col: str = ", ".join(cols)
38
47
  query: str = f"{self.CLAUSE} {col} FROM {self._table.__table_name__}"
39
48
  alias = ""
40
49
 
@@ -1,3 +1,4 @@
1
1
  from .max import Max # noqa: F401
2
+ from .min import Min # noqa: F401
2
3
  from .concat import Concat # noqa: F401
3
4
  from .group_by import GroupBy # noqa: F401
@@ -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 Min[T: tp.Type[Table]](DecompositionQueryBase[T], IAggregate[T]):
11
+ NAME: str = "MIN"
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 = "min") -> 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 = "min",
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})"
@@ -77,45 +77,27 @@ class Response[TFlavour, *Ts]:
77
77
  class MySQLRepository(IRepositoryBase[MySQLConnection]):
78
78
  def get_connection(func: Callable[..., Any]):
79
79
  @functools.wraps(func)
80
- def wrapper(self: IRepositoryBase[MySQLConnection], *args, **kwargs):
81
- self.connect()
82
- try:
83
- foo = func(self, *args, **kwargs)
84
- except Exception as e:
85
- self.connection.rollback()
86
- raise e
87
- finally:
88
- self.close_connection()
89
- return foo
80
+ def wrapper(self: MySQLRepository, *args, **kwargs):
81
+ with self._pool.get_connection() as cnx:
82
+ try:
83
+ foo = func(self, cnx._cnx, *args, **kwargs)
84
+ return foo
85
+ except Exception as e:
86
+ cnx._cnx.rollback()
87
+ raise e
90
88
 
91
89
  return wrapper
92
90
 
93
91
  def __init__(self, **kwargs: Any) -> None:
94
92
  self._data_config: dict[str, Any] = kwargs
95
93
  self._pool: MySQLConnectionPool = self.__create_MySQLConnectionPool()
96
- self._connection: PooledMySQLConnection = None
97
94
 
98
95
  def __create_MySQLConnectionPool(self):
99
96
  return MySQLConnectionPool(pool_name="mypool", pool_size=10, **self._data_config)
100
97
 
101
- @override
102
- def is_connected(self) -> bool:
103
- return self._connection._cnx is not None if self._connection else False
104
-
105
- @override
106
- def connect(self) -> None:
107
- self._connection = self._pool.get_connection()
108
- return None
109
-
110
- @override
111
- def close_connection(self) -> None:
112
- if self.is_connected():
113
- self._connection.close()
114
- return None
115
-
116
98
  @override
117
99
  @get_connection
118
- def read_sql[TFlavour](self, query: str, flavour: Type[TFlavour] = tuple, **kwargs) -> tuple[TFlavour]:
100
+ def read_sql[TFlavour](self, cnx: MySQLConnection, query: str, flavour: Type[TFlavour] = tuple, **kwargs) -> tuple[TFlavour]:
119
101
  """
120
102
  Return tuple of tuples by default.
121
103
 
@@ -125,7 +107,7 @@ class MySQLRepository(IRepositoryBase[MySQLConnection]):
125
107
  - flavour: Type[TFlavour]: Useful to return tuple of any Iterable type as dict,set,list...
126
108
  """
127
109
 
128
- with self._connection._cnx.cursor(buffered=True) as cursor:
110
+ with cnx.cursor(buffered=True) as cursor:
129
111
  cursor.execute(query)
130
112
  values: list[tuple] = cursor.fetchall()
131
113
  columns: tuple[str] = cursor.column_names
@@ -133,7 +115,7 @@ class MySQLRepository(IRepositoryBase[MySQLConnection]):
133
115
 
134
116
  # FIXME [ ]: this method does not comply with the implemented interface
135
117
  @get_connection
136
- def create_tables_code_first(self, path: str | Path) -> None:
118
+ def create_tables_code_first(self, cnx: MySQLConnection, path: str | Path) -> None:
137
119
  if not isinstance(path, Path | str):
138
120
  raise ValueError
139
121
 
@@ -148,33 +130,33 @@ class MySQLRepository(IRepositoryBase[MySQLConnection]):
148
130
  queries_list: list[str] = module_tree.get_queries()
149
131
 
150
132
  for query in queries_list:
151
- with self._connection._cnx.cursor(buffered=True) as cursor:
133
+ with cnx.cursor(buffered=True) as cursor:
152
134
  cursor.execute(query)
153
- self._connection._cnx.commit()
135
+ cnx.commit()
154
136
  return None
155
137
 
156
138
  @override
157
139
  @get_connection
158
- def executemany_with_values(self, query: str, values) -> None:
159
- with self._connection._cnx.cursor(buffered=True) as cursor:
140
+ def executemany_with_values(self, cnx: MySQLConnection, query: str, values) -> None:
141
+ with cnx.cursor(buffered=True) as cursor:
160
142
  cursor.executemany(query, values)
161
- self._connection._cnx.commit()
143
+ cnx.commit()
162
144
  return None
163
145
 
164
146
  @override
165
147
  @get_connection
166
- def execute_with_values(self, query: str, values) -> None:
167
- with self._connection._cnx.cursor(buffered=True) as cursor:
148
+ def execute_with_values(self, cnx: MySQLConnection, query: str, values) -> None:
149
+ with cnx.cursor(buffered=True) as cursor:
168
150
  cursor.execute(query, values)
169
- self._connection._cnx.commit()
151
+ cnx.commit()
170
152
  return None
171
153
 
172
154
  @override
173
155
  @get_connection
174
- def execute(self, query: str) -> None:
175
- with self._connection._cnx.cursor(buffered=True) as cursor:
156
+ def execute(self, cnx: MySQLConnection, query: str) -> None:
157
+ with cnx.cursor(buffered=True) as cursor:
176
158
  cursor.execute(query)
177
- self._connection._cnx.commit()
159
+ cnx.commit()
178
160
  return None
179
161
 
180
162
  @override
@@ -183,9 +165,9 @@ class MySQLRepository(IRepositoryBase[MySQLConnection]):
183
165
 
184
166
  @override
185
167
  @get_connection
186
- def database_exists(self, name: str) -> bool:
168
+ def database_exists(self, cnx: MySQLConnection, name: str) -> bool:
187
169
  query = "SHOW DATABASES LIKE %s;"
188
- with self._connection._cnx.cursor(buffered=True) as cursor:
170
+ with cnx.cursor(buffered=True) as cursor:
189
171
  cursor.execute(query, (name,))
190
172
  res = cursor.fetchmany(1)
191
173
  return len(res) > 0
@@ -196,12 +178,12 @@ class MySQLRepository(IRepositoryBase[MySQLConnection]):
196
178
 
197
179
  @override
198
180
  @get_connection
199
- def table_exists(self, name: str) -> bool:
200
- if not self._connection._cnx.database:
181
+ def table_exists(self, cnx: MySQLConnection, name: str) -> bool:
182
+ if not cnx.database:
201
183
  raise Exception("No database selected")
202
184
 
203
185
  query = "SHOW TABLES LIKE %s;"
204
- with self._connection._cnx.cursor(buffered=True) as cursor:
186
+ with cnx.cursor(buffered=True) as cursor:
205
187
  cursor.execute(query, (name,))
206
188
  res = cursor.fetchmany(1)
207
189
  return len(res) > 0
@@ -210,10 +192,6 @@ class MySQLRepository(IRepositoryBase[MySQLConnection]):
210
192
  def create_database(self, name: str, if_exists: TypeExists = "fail") -> None:
211
193
  return CreateDatabase(self).execute(name, if_exists)
212
194
 
213
- @override
214
- @property
215
- def connection(self) -> MySQLConnection:
216
- return self._connection
217
195
 
218
196
  @property
219
197
  def database(self) -> Optional[str]:
@@ -1,5 +1,8 @@
1
1
  from __future__ import annotations
2
- from typing import override, Type, TYPE_CHECKING, Any, Callable, Optional
2
+ from typing import Iterable, override, Type, TYPE_CHECKING, Any, Callable, Optional
3
+ import inspect
4
+ from mysql.connector import MySQLConnection, errors, errorcode
5
+
3
6
 
4
7
  if TYPE_CHECKING:
5
8
  from ormlambda import Table
@@ -7,6 +10,8 @@ if TYPE_CHECKING:
7
10
  from ormlambda.common.interfaces.IStatements import OrderType
8
11
  from ormlambda.common.interfaces import IQuery, IRepositoryBase, IStatements_two_generic
9
12
  from ormlambda.common.interfaces.IRepositoryBase import TypeExists
13
+ from ormlambda.common.interfaces import IAggregate
14
+ from ormlambda.common.interfaces.IStatements import WhereTypes
10
15
 
11
16
  from ormlambda import AbstractSQLStatements
12
17
  from .clauses import DeleteQuery
@@ -23,13 +28,10 @@ from .clauses import WhereCondition
23
28
  from .clauses import Count
24
29
  from .clauses import GroupBy
25
30
 
26
- from mysql.connector import MySQLConnection, errors, errorcode
27
-
28
-
29
- import inspect
30
31
 
31
32
  from ormlambda.utils import ForeignKey, Table
32
33
  from ormlambda.common.enums import JoinType
34
+ from . import functions as func
33
35
 
34
36
 
35
37
  class MySQLStatements[T: Table](AbstractSQLStatements[T, MySQLConnection]):
@@ -140,9 +142,15 @@ class MySQLStatements[T: Table](AbstractSQLStatements[T, MySQLConnection]):
140
142
  return self
141
143
 
142
144
  @override
143
- def where(self, lambda_: Callable[[T], bool] = lambda: None, **kwargs) -> IStatements_two_generic[T, MySQLConnection]:
145
+ def where(self, conditions: WhereTypes = lambda: None, **kwargs) -> IStatements_two_generic[T, MySQLConnection]:
144
146
  # FIXME [x]: I've wrapped self._model into tuple to pass it instance attr. Idk if it's correct
145
- where_query = WhereCondition[T](function=lambda_, instances=(self._model,), **kwargs)
147
+
148
+ if isinstance(conditions, Iterable):
149
+ for x in conditions:
150
+ self._query_list["where"].append(WhereCondition[T](function=x, instances=(self._model,), **kwargs))
151
+ return self
152
+
153
+ where_query = WhereCondition[T](function=conditions, instances=(self._model,), **kwargs)
146
154
  self._query_list["where"].append(where_query)
147
155
  return self
148
156
 
@@ -152,6 +160,18 @@ class MySQLStatements[T: Table](AbstractSQLStatements[T, MySQLConnection]):
152
160
  self._query_list["order"].append(order)
153
161
  return self
154
162
 
163
+ @override
164
+ def concat[*Ts](self, selector: Callable[[T], tuple[*Ts]], alias: bool = True, alias_name: str = "CONCAT") -> IAggregate[T]:
165
+ return func.Concat[T](self._model, selector, alias=alias, alias_name=alias_name)
166
+
167
+ @override
168
+ def max[TProp](self, column: Callable[[T], TProp], alias: bool = True, alias_name: str = "max") -> TProp:
169
+ return func.Max[T](self._model, column=column, alias=alias, alias_name=alias_name)
170
+
171
+ @override
172
+ def min[TProp](self, column: Callable[[T], TProp], alias: bool = True, alias_name: str = "min") -> TProp:
173
+ return func.Min[T](self._model, column=column, alias=alias, alias_name=alias_name)
174
+
155
175
  @override
156
176
  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):
157
177
  if len(inspect.signature(selector).parameters) == 0:
@@ -198,24 +218,33 @@ class MySQLStatements[T: Table](AbstractSQLStatements[T, MySQLConnection]):
198
218
  def _build(self) -> str:
199
219
  query: str = ""
200
220
 
201
- # self.__create_necessary_inner_join()
202
221
  for x in self.__order__:
203
222
  sub_query: Optional[list[IQuery]] = self._query_list.get(x, None)
204
- if sub_query is not None:
205
- if isinstance(sub_query[0], WhereCondition):
206
- query_ = self.__build_where_clause(sub_query)
207
-
208
- # we must check if any join already exists on query string
209
- elif isinstance(sub_query[0], JoinSelector):
210
- select_query: str = self._query_list["select"][0].query
211
- query_ = ""
212
- for join in sub_query:
213
- if join.query not in select_query:
214
- query_ += f"\n{join.query}"
215
- else:
216
- query_ = "\n".join([x.query for x in sub_query])
217
-
218
- query += f"\n{query_}" if query != "" else query_
223
+ if sub_query is None:
224
+ continue
225
+
226
+ if isinstance(sub_query[0], WhereCondition):
227
+ query_ = self.__build_where_clause(sub_query)
228
+
229
+ # we must check if any join already exists on query string
230
+ elif isinstance(sub_query[0], JoinSelector):
231
+ select_query: str = self._query_list["select"][0].query
232
+ query_ = ""
233
+ for join in sub_query:
234
+ if join.query not in select_query:
235
+ query_ += f"\n{join.query}"
236
+
237
+ elif isinstance((select := sub_query[0]), Select):
238
+ query_: str = ""
239
+ where_joins = self.__create_necessary_inner_join()
240
+ if where_joins:
241
+ select._fk_relationship.update(where_joins)
242
+ query_ = select.query
243
+
244
+ else:
245
+ query_ = "\n".join([x.query for x in sub_query])
246
+
247
+ query += f"\n{query_}" if query != "" else query_
219
248
  self._query_list.clear()
220
249
  return query
221
250
 
@@ -227,3 +256,19 @@ class MySQLStatements[T: Table](AbstractSQLStatements[T, MySQLConnection]):
227
256
  and_, clause = q.split(" ", maxsplit=1)
228
257
  query += f" {and_} ({clause})"
229
258
  return query
259
+
260
+ def __create_necessary_inner_join(self) -> Optional[set[tuple[Type[Table], Type[Table]]]]:
261
+ # 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.
262
+ if "where" not in self._query_list:
263
+ return None
264
+
265
+ res = []
266
+ for where in self._query_list["where"]:
267
+ where: AbstractWhere
268
+
269
+ tables = where.get_involved_tables()
270
+
271
+ if tables:
272
+ [res.append(x) for x in tables]
273
+
274
+ return set(res)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ormlambda
3
- Version: 2.6.0
3
+ Version: 2.7.2
4
4
  Summary: ORM designed to interact with the database (currently with MySQL) using lambda functions and nested functions
5
5
  Author: p-hzamora
6
6
  Author-email: p.hzamora@icloud.com
@@ -1,19 +1,19 @@
1
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=1v86bTuSxsFzy5RUc9HnraRVjbGezJoqLbizcrMo-Uc,4548
5
- ormlambda/common/abstract_classes/decomposition_query.py,sha256=OReQs1SEf02FnJI8SDreXp2agkjRqyj_RJrFiMn55ro,11409
4
+ ormlambda/common/abstract_classes/abstract_model.py,sha256=0mGoW-MF0bX1VrAp2AJZ8Nk_BV7N_2NtzZoUFCg5Wm4,4595
5
+ ormlambda/common/abstract_classes/decomposition_query.py,sha256=c-Vo0f2dnVCv9Qsmqt9AZaZAYvyYhtZFdc3HKyHCMZ0,11396
6
6
  ormlambda/common/abstract_classes/non_query_base.py,sha256=5jhvyT7OZaJxlGp9XMP3vQ4ei5QQZBn-fFtJnD640mE,980
7
7
  ormlambda/common/abstract_classes/query_base.py,sha256=6qUFPwsVx45kUW3b66pHiSyjhcH4mzbdkddlGeUnG7c,266
8
8
  ormlambda/common/enums/__init__.py,sha256=4lVKCHi1JalwgNzjsAXqX-C54NJEH83y2v5baMO8fN4,103
9
9
  ormlambda/common/enums/condition_types.py,sha256=mDPMrtzZu4IYv3-q5Zw4NNgwUoNAx4lih5SIDFRn1Qo,309
10
10
  ormlambda/common/enums/join_type.py,sha256=7LEhE34bzYOwJxe8yxPJo_EjQpGylgClAPX1Wt3z4n4,292
11
11
  ormlambda/common/interfaces/IAggregate.py,sha256=i9zZIYrstP2oolUYqR-udeQQl7M0NwSzrr-VUL0b5HI,271
12
- ormlambda/common/interfaces/IDecompositionQuery.py,sha256=VwZdYhVApNyGrWgqr27mRMOxREMb6Xv-oYtW_0c2D2g,1365
12
+ ormlambda/common/interfaces/IDecompositionQuery.py,sha256=dHdCxezbWwy-c4U4po2hQB8f_wa0wGFvA2TKaHBx0Do,1363
13
13
  ormlambda/common/interfaces/INonQueryCommand.py,sha256=7CjLW4sKqkR5zUIGvhRXOtzTs6vypJW1a9EJHlgCw2c,260
14
14
  ormlambda/common/interfaces/IQueryCommand.py,sha256=hfzCosK4-n8RJIb2PYs8b0qU3TNmfYluZXBf47KxxKs,331
15
- ormlambda/common/interfaces/IRepositoryBase.py,sha256=iWZ9X2qjvacZHdbykrE5wAMyi-4LJLKcNvtNTGD6S-8,1398
16
- ormlambda/common/interfaces/IStatements.py,sha256=zsjR6JnQ57M6GPGRqAXkuuwZx6fh3Mq6NB4D6gWw-3w,10028
15
+ ormlambda/common/interfaces/IRepositoryBase.py,sha256=zGm7tDrFLKj3FMYN-MWI-em3INws27g2yByNYYMx0qk,1126
16
+ ormlambda/common/interfaces/IStatements.py,sha256=dlH8rL0flsF6QuvDsOb3LuM-FhtO9_D64SDjz07f3zg,10296
17
17
  ormlambda/common/interfaces/__init__.py,sha256=00ca9a-u_A8DzyEyxPfBMxfqLKFzzUgJaeNmoRitAbA,360
18
18
  ormlambda/components/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
19
  ormlambda/components/delete/IDelete.py,sha256=06ZEdbKBxsHSwsGMBu0E1om4WJjojZAm-L3b95eQrcc,139
@@ -42,17 +42,18 @@ ormlambda/databases/my_sql/clauses/insert.py,sha256=LO9H8VVK3j62dICXqpEUXKxOHPxk
42
42
  ormlambda/databases/my_sql/clauses/joins.py,sha256=U6JnUvQo7AXyEeK-X1jMvckXefgAB7ugSmJCZhH1XQI,3058
43
43
  ormlambda/databases/my_sql/clauses/limit.py,sha256=a4lI8FVRKpfXwBQTXdkbVtlQkmzcjE20ymiCy1IaSc4,391
44
44
  ormlambda/databases/my_sql/clauses/offset.py,sha256=81170JhsQndjKlDfQj1ll-tiYHQbW8SuU4IE3mhQF7Y,395
45
- ormlambda/databases/my_sql/clauses/order.py,sha256=AcTD7aeaaz-isyjs1_CkRCb7z8b7o5GhpMxc_2G5IgI,1030
46
- ormlambda/databases/my_sql/clauses/select.py,sha256=NQFBoxTeJxFBEJJ8SIKr_06BYEeblOuP4J_9YD_iJxI,1280
45
+ ormlambda/databases/my_sql/clauses/order.py,sha256=RVqyEtD0InnOPAmRvjtNJ-vrAy0V9OHMLoFB8TkpX0I,1137
46
+ ormlambda/databases/my_sql/clauses/select.py,sha256=unJBteFK5D360KnIPyqz8OMaVJqXJlI5Kkp6u0JJ1vU,1574
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=UgU5vnTqFAx91wKwnECpww5tETDZ9F7IdC_SiZOgZhI,8336
50
- ormlambda/databases/my_sql/functions/__init__.py,sha256=jUFHlFc04UAPht65GlEkLXa5_dwfBl2c377ohrNXswc,120
50
+ ormlambda/databases/my_sql/functions/__init__.py,sha256=XpLhHssbw43-0NRDpY0T9SKhyedzCQOCGQWdrfHvbVw,155
51
51
  ormlambda/databases/my_sql/functions/concat.py,sha256=cZztl5eSATpYMKVKfyPbul6OocaUkaEGpt3uWmhf-3o,1177
52
52
  ormlambda/databases/my_sql/functions/group_by.py,sha256=ym1L6RBA70hmzrkJ-IA_-6Esq7eoVgNdf1vWpqxRq-8,1080
53
53
  ormlambda/databases/my_sql/functions/max.py,sha256=zrO_RBKsHhyokEmSpPI6Yg5OY6Jf4GGp2RveBJdOuuA,1190
54
- ormlambda/databases/my_sql/repository.py,sha256=3IQX8z6g5N1e7FVFCr-kEnKoi_E_N3aCQ_MA5q_bHnc,7566
55
- ormlambda/databases/my_sql/statements.py,sha256=4o1NguIiNOVA9i8hqaUMJdIfgn1MvXnnJhJABKPJtF0,9660
54
+ ormlambda/databases/my_sql/functions/min.py,sha256=SEVuUdIJNz9zM5za1kLTWalFkhErjsjyBb8SU8n0F94,1190
55
+ ormlambda/databases/my_sql/repository.py,sha256=fSAAmO_T5mC1bodOVZn5CY9JSfRq4VhsdlWT1JLl7Zw,6927
56
+ ormlambda/databases/my_sql/statements.py,sha256=NTAZs9TDEI_KoH0GNG2Ea8oTFckXeTfTg_cdUHSYziA,11493
56
57
  ormlambda/model_base.py,sha256=RrAATQWX_bHJ0ZQ5sCHyJKA4NiR7ZJY4I6dqhnGWWAc,1216
57
58
  ormlambda/utils/__init__.py,sha256=ywMdWqmA2jHj19-W-S04yfaYF5hv4IZ1lZDq0B8Jnjs,142
58
59
  ormlambda/utils/column.py,sha256=5FAGzCU4yvNS4MhwJJ5i73h7RvHD5UCVox0NdzMsMiE,1945
@@ -69,7 +70,7 @@ ormlambda/utils/module_tree/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMp
69
70
  ormlambda/utils/module_tree/dfs_traversal.py,sha256=lSF03G63XtJFLp03ueAmsHMBvhUkjptDbK3IugXm8iU,1425
70
71
  ormlambda/utils/module_tree/dynamic_module.py,sha256=zwvjU3U2cz6H2CDp9Gncs5D5bSAyfITNa2SDqFDl8rw,8551
71
72
  ormlambda/utils/table_constructor.py,sha256=8Apm44K6MiYMK3PQyK74MUV18OatbFI9eDLAVklQO0w,11660
72
- ormlambda-2.6.0.dist-info/LICENSE,sha256=xBprFw8GJLdHMOoUqDk0427EvjIcbEREvXXVFULuuXU,1080
73
- ormlambda-2.6.0.dist-info/METADATA,sha256=HtLgvKuUEcQqDadfokBM0qyxTjTUSNOoErjtJ3aMdGk,8428
74
- ormlambda-2.6.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
75
- ormlambda-2.6.0.dist-info/RECORD,,
73
+ ormlambda-2.7.2.dist-info/LICENSE,sha256=xBprFw8GJLdHMOoUqDk0427EvjIcbEREvXXVFULuuXU,1080
74
+ ormlambda-2.7.2.dist-info/METADATA,sha256=8CXacsolfAa439Blpjufzi4bSOTOjked1fz4C_4gYZ4,8428
75
+ ormlambda-2.7.2.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
76
+ ormlambda-2.7.2.dist-info/RECORD,,