ormlambda 3.7.2__py3-none-any.whl → 3.11.1__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.
@@ -0,0 +1,17 @@
1
+ from typing import Optional, Any, TypeGuard, Iterable
2
+ import collections.abc as collections_abc
3
+
4
+
5
+ def is_non_string_iterable(obj: Any) -> TypeGuard[Iterable[Any]]:
6
+ return isinstance(obj, collections_abc.Iterable) and not isinstance(obj, (str, bytes))
7
+
8
+
9
+ def to_list(x: Any, default: Optional[list[Any]] = None) -> list[Any]:
10
+ if x is None:
11
+ return default # type: ignore
12
+ if not is_non_string_iterable(x):
13
+ return [x]
14
+ elif isinstance(x, list):
15
+ return x
16
+ else:
17
+ return list(x)
@@ -6,7 +6,6 @@ import abc
6
6
 
7
7
  class BaseRepository[TPool](IRepositoryBase):
8
8
  def __init__[TArgs](self, pool: Type[TPool], **kwargs: Unpack[TArgs]) -> None:
9
- self._data_config: dict[str, str] = kwargs
10
9
  self._pool: TPool = pool(**kwargs)
11
10
 
12
11
  @contextlib.contextmanager
ormlambda/sql/column.py CHANGED
@@ -1,5 +1,5 @@
1
1
  from __future__ import annotations
2
- from typing import Iterable, Type, Optional, TYPE_CHECKING
2
+ from typing import Iterable, Type, Optional, TYPE_CHECKING, overload
3
3
  import abc
4
4
  from ormlambda.sql.types import TableType, ComparerType, ColumnType
5
5
  from ormlambda import ConditionType
@@ -25,18 +25,41 @@ class Column[TProp]:
25
25
  "_check",
26
26
  )
27
27
 
28
+ @overload
29
+ def __init__(self, *, column_name: str): ...
30
+
31
+ @overload
28
32
  def __init__[T: Table](
29
33
  self,
30
34
  dtype: Type[TProp],
35
+ *,
31
36
  is_primary_key: bool = False,
32
37
  is_auto_generated: bool = False,
33
38
  is_auto_increment: bool = False,
34
39
  is_unique: bool = False,
35
40
  check_types: bool = True,
41
+ ) -> None: ...
42
+
43
+ def __init__[T: Table](
44
+ self,
45
+ dtype: Optional[Type[TProp]] = None,
46
+ *,
47
+ is_primary_key: bool = False,
48
+ is_auto_generated: bool = False,
49
+ is_auto_increment: bool = False,
50
+ is_unique: bool = False,
51
+ check_types: bool = True,
52
+ column_name: Optional[str] = None,
36
53
  ) -> None:
54
+ if dtype is None and column_name is None:
55
+ raise AttributeError("You must specify either the 'dtype' or 'column_name' attribute.")
56
+
57
+ if column_name is not None:
58
+ dtype = type(column_name)
59
+
37
60
  self.dtype: Type[TProp] = dtype
38
61
  self.table: Optional[TableType[T]] = None
39
- self.column_name: Optional[str] = None
62
+ self.column_name: Optional[str] = column_name
40
63
  self.__private_name: Optional[str] = None
41
64
  self._check = check_types
42
65
 
@@ -70,7 +93,9 @@ class Column[TProp]:
70
93
  def __hash__(self) -> int:
71
94
  return hash(
72
95
  (
96
+ self.dtype,
73
97
  self.column_name,
98
+ self.table,
74
99
  self.is_primary_key,
75
100
  self.is_auto_generated,
76
101
  self.is_auto_increment,
@@ -1,5 +1,5 @@
1
1
  from __future__ import annotations
2
- from typing import Callable, TYPE_CHECKING, Optional, Any, Type, overload
2
+ from typing import Callable, TYPE_CHECKING, Optional, Any, Type, cast, overload
3
3
 
4
4
  from ormlambda.common.interfaces.IQueryCommand import IQuery
5
5
 
@@ -10,15 +10,45 @@ if TYPE_CHECKING:
10
10
  from ormlambda.sql.clause_info.clause_info_context import ClauseContextType
11
11
 
12
12
 
13
+ class ForeignKeyContext(set["ForeignKey"]):
14
+ def clear(self):
15
+ to_remove = {x for x in self if not cast(ForeignKey, x)._keep_alive}
16
+ for el in to_remove:
17
+ self.remove(el)
18
+
19
+ def remove(self, element):
20
+ return super().remove(element)
21
+
22
+ def pop(self, item):
23
+ for el in self:
24
+ if el != item:
25
+ continue
26
+
27
+ if not cast(ForeignKey, el)._keep_alive:
28
+ super().remove(el)
29
+ return el
30
+
31
+ def add(self, element):
32
+ return super().add(element)
33
+
34
+
13
35
  class ForeignKey[TLeft: Table, TRight: Table](IQuery):
14
- stored_calls: set[ForeignKey] = set()
36
+ stored_calls: ForeignKeyContext = ForeignKeyContext()
15
37
 
16
38
  @overload
17
39
  def __new__[LProp, RProp](self, comparer: Comparer[LProp, RProp], clause_name: str) -> None: ...
18
40
  @overload
19
- def __new__[LProp, TRight, RProp](cls, tright: Type[TRight], relationship: Callable[[TLeft, TRight], Any | Comparer[TLeft, LProp, TRight, RProp]]) -> TRight: ...
41
+ def __new__[LProp, TRight, RProp](cls, tright: Type[TRight], relationship: Callable[[TLeft, TRight], Any | Comparer[TLeft, LProp, TRight, RProp]], keep_alive: bool = ...) -> TRight: ...
20
42
 
21
- def __new__[LProp, TRight, RProp](cls, tright: Optional[TRight] = None, relationship: Optional[Callable[[TLeft, TRight], Any | Comparer[TLeft, LProp, TRight, RProp]]] = None, *, comparer: Optional[Comparer] = None, clause_name: Optional[str] = None) -> TRight:
43
+ def __new__[LProp, TRight, RProp](
44
+ cls,
45
+ tright: Optional[TRight] = None,
46
+ relationship: Optional[Callable[[TLeft, TRight], Any | Comparer[TLeft, LProp, TRight, RProp]]] = None,
47
+ *,
48
+ comparer: Optional[Comparer] = None,
49
+ clause_name: Optional[str] = None,
50
+ keep_alive: bool = False,
51
+ ) -> TRight:
22
52
  return super().__new__(cls)
23
53
 
24
54
  def __init__[LProp, RProp](
@@ -28,7 +58,9 @@ class ForeignKey[TLeft: Table, TRight: Table](IQuery):
28
58
  *,
29
59
  comparer: Optional[Comparer] = None,
30
60
  clause_name: Optional[str] = None,
61
+ keep_alive: bool = False,
31
62
  ) -> None:
63
+ self._keep_alive = keep_alive
32
64
  if comparer is not None and clause_name is not None:
33
65
  self.__init__with_comparer(comparer, clause_name)
34
66
  else:
@@ -27,6 +27,7 @@ from ..types import (
27
27
  Select10,
28
28
  TypeExists,
29
29
  WhereTypes,
30
+ SelectCols,
30
31
  )
31
32
 
32
33
 
@@ -130,12 +131,12 @@ class IStatements[T: Table](ABC):
130
131
  # endregion
131
132
  # region count
132
133
  @abstractmethod
133
- def count(
134
+ def count[TProp](
134
135
  self,
135
- selection: Callable[[T], tuple] = lambda x: "*",
136
- alias_clause="count",
136
+ selection: None | SelectCols[T, TProp] = ...,
137
+ alias: str = ...,
137
138
  execute: bool = False,
138
- ) -> Optional[IStatements[T]]: ...
139
+ ) -> Optional[int]: ...
139
140
 
140
141
  # endregion
141
142
  # region delete
@@ -161,46 +162,52 @@ class IStatements[T: Table](ABC):
161
162
  def where[LProp, RTable, RProp](self, conditions: WhereTypes[T, LProp, RTable, RProp] = None) -> IStatements[T]: ...
162
163
 
163
164
  # endregion
164
- # region order
165
- @overload
166
- def order[TValue](self, _lambda_col: Callable[[T], TValue]) -> IStatements[T]: ...
165
+
166
+ # region having
167
+
167
168
  @overload
168
- def order[TValue](self, _lambda_col: Callable[[T], TValue], order_type: OrderTypes) -> IStatements[T]: ...
169
+ def having[LProp, RTable, RProp](self, conditions: Callable[[T], WhereTypes[T, LProp, RTable, RProp]]) -> IStatements[T]: ...
170
+
169
171
  @abstractmethod
170
- def order[TValue](self, _lambda_col: Callable[[T], TValue], order_type: OrderTypes) -> IStatements[T]: ...
172
+ def having[LProp, RTable, RProp](self, conditions: WhereTypes[T, LProp, RTable, RProp] = None) -> IStatements[T]: ...
173
+
174
+ # endregion
175
+ # region order
176
+ @abstractmethod
177
+ def order[TValue](self, columns: SelectCols[T, TValue], order_type: OrderTypes) -> IStatements[T]: ...
171
178
 
172
179
  # endregion
173
180
  # region concat
174
181
  @overload
175
- def concat[P](self, selector: Callable[[T], tuple[P]]) -> IAggregate: ...
182
+ def concat(self, selector: SelectCols[T, str], alias: str = "concat") -> IAggregate: ...
176
183
 
177
184
  # endregion
178
185
  # region max
179
- @overload
186
+ @abstractmethod
180
187
  def max[TProp](
181
188
  self,
182
- column: Callable[[T], TProp],
189
+ column: SelectCols[T, TProp],
183
190
  alias: Optional[str] = ...,
184
- execute: bool = ...,
185
- ) -> TProp: ...
191
+ execute: bool = False,
192
+ ) -> int: ...
186
193
  # endregion
187
194
  # region min
188
- @overload
195
+ @abstractmethod
189
196
  def min[TProp](
190
197
  self,
191
- column: Callable[[T], TProp],
198
+ column: SelectCols[T, TProp],
192
199
  alias: Optional[str] = ...,
193
- execute: bool = ...,
194
- ) -> TProp: ...
200
+ execute: bool = False,
201
+ ) -> int: ...
195
202
  # endregion
196
203
  # region sum
197
- @overload
204
+ @abstractmethod
198
205
  def sum[TProp](
199
206
  self,
200
- column: Callable[[T], TProp],
207
+ column: SelectCols[T, TProp],
201
208
  alias: Optional[str] = ...,
202
- execute: bool = ...,
203
- ) -> TProp: ...
209
+ execute: bool = False,
210
+ ) -> int: ...
204
211
 
205
212
  @overload
206
213
  def join[FKTable](self, joins: TupleJoinType[FKTable] | tuple[*TupleJoinType[FKTable]]) -> JoinContext[tuple[*TupleJoinType[FKTable]]]: ...
@@ -208,6 +215,8 @@ class IStatements[T: Table](ABC):
208
215
  # endregion
209
216
  # region select
210
217
  type SelectorType[TOri, *T] = Callable[[TOri], tuple[*T]] | tuple[*T]
218
+ type SelectorFlavourType[T, TResponse] = Optional[Callable[[T], TResponse]] | TResponse
219
+ type SelectorOneType[T, TResponse] = Callable[[T, TResponse]] | TResponse
211
220
 
212
221
  @overload
213
222
  def select[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10](self, selector: SelectorType[T, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], *, by: Optional[Enum] = ...) -> Select10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]: ...
@@ -234,8 +243,6 @@ class IStatements[T: Table](ABC):
234
243
  @overload
235
244
  def select(self) -> Tuple[T]: ...
236
245
 
237
- type SelectorFlavourType[T, TResponse] = Optional[Callable[[T], TResponse]]
238
-
239
246
  # @overload
240
247
  # def select[TFlavour](self, selector: Optional[Callable[[T], tuple]] = ..., *, cast_to_tuple: bool = ..., flavour: Type[TFlavour], by: Optional[Enum] = ..., **kwargs) -> TFlavour: ...
241
248
  @overload
@@ -243,7 +250,7 @@ class IStatements[T: Table](ABC):
243
250
  @overload
244
251
  def select[*TRes](self, selector: SelectorFlavourType[T, tuple[*TRes]] = ..., *, flavour: Type[tuple], by: Optional[Enum] = ..., **kwargs) -> tuple[tuple[*TRes]]: ...
245
252
  @overload
246
- def select[TFlavour](self, selector: SelectorFlavourType[T, tuple] = ..., *, flavour: Type[TFlavour], by: Optional[Enum] = ..., **kwargs) -> tuple[TFlavour]: ...
253
+ def select[TFlavour](self, selector: SelectorFlavourType[T, tuple] = ..., *, flavour: Type[TFlavour], by: Optional[Enum] = ..., **kwargs) -> tuple[TFlavour, ...]: ...
247
254
 
248
255
  @abstractmethod
249
256
  def select[TValue, TFlavour, P](self, selector: SelectorFlavourType[T, tuple[TValue, P]] = ..., *, cast_to_tuple: bool = ..., flavour: Type[TFlavour] = ..., by: JoinType = ..., **kwargs): ...
@@ -255,21 +262,21 @@ class IStatements[T: Table](ABC):
255
262
  @overload
256
263
  def select_one[TFlavour](self, *, by: Optional[Enum] = ..., flavour: Type[TFlavour], **kwargs) -> TFlavour: ...
257
264
  @overload
258
- def select_one[T1](self, selector: Callable[[T], T1 | tuple[T1]], *, by: Optional[Enum] = ...) -> T1: ...
265
+ def select_one[T1](self, selector: SelectorOneType[T, T1 | tuple[T1]], *, by: Optional[Enum] = ...) -> T1: ...
259
266
  @overload
260
- def select_one[*TRes](self, selector: Callable[[T], tuple[*TRes]], *, by: Optional[Enum] = ...) -> tuple[*TRes]: ...
267
+ def select_one[*TRes](self, selector: SelectorOneType[T, tuple[*TRes]], *, by: Optional[Enum] = ...) -> tuple[*TRes]: ...
261
268
  @overload
262
- def select_one[T1](self, selector: Callable[[T], tuple[T1]], *, by: Optional[Enum] = ..., flavour: Type, **kwargs) -> T1: ...
269
+ def select_one[T1](self, selector: SelectorOneType[T, tuple[T1]], *, by: Optional[Enum] = ..., flavour: Type, **kwargs) -> T1: ...
263
270
  @overload
264
- def select_one[T1, TFlavour](self, selector: Callable[[T], T1], *, by: Optional[Enum] = ..., flavour: Type[TFlavour], **kwargs) -> TFlavour: ...
271
+ def select_one[T1, TFlavour](self, selector: SelectorOneType[T, T1], *, by: Optional[Enum] = ..., flavour: Type[TFlavour], **kwargs) -> TFlavour: ...
265
272
  @overload
266
- def select_one[*TRest](self, selector: Callable[[T], tuple[*TRest]], *, by: Optional[Enum] = ..., flavour: Type[tuple], **kwargs) -> tuple[*TRest]: ...
273
+ def select_one[*TRest](self, selector: SelectorOneType[T, tuple[*TRest]], *, by: Optional[Enum] = ..., flavour: Type[tuple], **kwargs) -> tuple[*TRest]: ...
267
274
  @overload
268
- def select_one[TFlavour](self, selector: Callable[[T], tuple], *, by: Optional[Enum] = ..., flavour: Type[TFlavour], **kwargs) -> TFlavour: ...
275
+ def select_one[TFlavour](self, selector: SelectorOneType[T, tuple], *, by: Optional[Enum] = ..., flavour: Type[TFlavour], **kwargs) -> TFlavour: ...
269
276
  @abstractmethod
270
277
  def select_one[TValue, TFlavour, *TRest](
271
278
  self,
272
- selector: Optional[Callable[[T], tuple[TValue, *TRest]]] = lambda: None,
279
+ selector: Optional[SelectorOneType[T, tuple[TValue, *TRest]]] = lambda: None,
273
280
  *,
274
281
  flavour: Type[TFlavour] = ...,
275
282
  by: Optional[Enum] = ...,
@@ -281,23 +288,23 @@ class IStatements[T: Table](ABC):
281
288
  @overload
282
289
  def first(self) -> T: ...
283
290
  @overload
284
- def first[TFlavour](self, *, by: Optional[Enum] = ..., flavour: Type[TFlavour], **kwargs) -> TFlavour: ...
291
+ def first[T1](self, selector: SelectorOneType[T, T1 | tuple[T1]], *, by: Optional[Enum] = ...) -> T1: ...
285
292
  @overload
286
- def first[T1](self, selector: Callable[[T], T1 | tuple[T1]], *, by: Optional[Enum] = ...) -> T1: ...
293
+ def first[*TRes](self, selector: SelectorOneType[T, tuple[*TRes]], *, by: Optional[Enum] = ...) -> tuple[*TRes]: ...
287
294
  @overload
288
- def first[*TRes](self, selector: Callable[[T], tuple[*TRes]], *, by: Optional[Enum] = ...) -> tuple[*TRes]: ...
295
+ def first[TFlavour](self, *, by: Optional[Enum] = ..., flavour: Type[TFlavour], **kwargs) -> TFlavour: ...
289
296
  @overload
290
- def first[T1](self, selector: Callable[[T], tuple[T1]], *, by: Optional[Enum] = ..., flavour: Type, **kwargs) -> T1: ...
297
+ def first[T1](self, selector: SelectorOneType[T, tuple[T1]], *, by: Optional[Enum] = ..., flavour: Type, **kwargs) -> T1: ...
291
298
  @overload
292
- def first[T1, TFlavour](self, selector: Callable[[T], T1], *, by: Optional[Enum] = ..., flavour: Type[TFlavour], **kwargs) -> TFlavour: ...
299
+ def first[T1, TFlavour](self, selector: SelectorOneType[T, T1], *, by: Optional[Enum] = ..., flavour: Type[TFlavour], **kwargs) -> TFlavour: ...
293
300
  @overload
294
- def first[*TRest](self, selector: Callable[[T], tuple[*TRest]], *, by: Optional[Enum] = ..., flavour: Type[tuple], **kwargs) -> tuple[*TRest]: ...
301
+ def first[*TRest](self, selector: SelectorOneType[T, tuple[*TRest]], *, by: Optional[Enum] = ..., flavour: Type[tuple], **kwargs) -> tuple[*TRest]: ...
295
302
  @overload
296
- def first[TFlavour](self, selector: Callable[[T], tuple], *, by: Optional[Enum] = ..., flavour: Type[TFlavour], **kwargs) -> TFlavour: ...
303
+ def first[TFlavour](self, selector: SelectorOneType[T, tuple], *, by: Optional[Enum] = ..., flavour: Type[TFlavour], **kwargs) -> TFlavour: ...
297
304
  @abstractmethod
298
305
  def first[TValue, TFlavour, *TRest](
299
306
  self,
300
- selector: Optional[Callable[[T], tuple[TValue, *TRest]]] = lambda: None,
307
+ selector: Optional[SelectorOneType[T, tuple[TValue, *TRest]]] = lambda: None,
301
308
  *,
302
309
  flavour: Type[TFlavour] = ...,
303
310
  by: Optional[Enum] = ...,
@@ -305,9 +312,9 @@ class IStatements[T: Table](ABC):
305
312
 
306
313
  # endregion
307
314
 
308
- # region group_by
315
+ # region groupby
309
316
  @abstractmethod
310
- def group_by[TRepo](self, column: Callable[[T], TRepo]) -> IStatements[T]: ...
317
+ def groupby[TRepo](self, column: Callable[[T], TRepo]) -> IStatements[T]: ...
311
318
 
312
319
  # endregion
313
320
 
@@ -10,15 +10,18 @@ from typing import (
10
10
  import enum
11
11
 
12
12
 
13
- from ormlambda.common.enums import JoinType
14
-
15
13
  if TYPE_CHECKING:
14
+ from ormlambda.common.enums import JoinType
16
15
  from ormlambda.sql.comparer import Comparer
16
+ from ormlambda.sql.types import ColumnType
17
17
 
18
18
  type OrderTypes = Literal["ASC", "DESC"] | OrderType | Iterable[OrderType]
19
19
 
20
20
 
21
- class OrderType(enum.Enum):
21
+ class OrderType(str, enum.Enum):
22
+ def __str__(self):
23
+ return super().__str__()
24
+
22
25
  ASC = "ASC"
23
26
  DESC = "DESC"
24
27
 
@@ -49,3 +52,6 @@ type WhereTypes[LTable, LProp, RTable, RProp] = Union[
49
52
  tuple[Comparer[LTable, LProp, RTable, RProp], ...],
50
53
  Callable[[LTable], WhereTypes[LTable, LProp, RTable, RProp]],
51
54
  ]
55
+
56
+
57
+ type SelectCols[T, TProp] = Callable[[T], ColumnType[TProp]] | ColumnType[TProp]
@@ -1,15 +1,13 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ormlambda
3
- Version: 3.7.2
3
+ Version: 3.11.1
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
7
7
  Requires-Python: >=3.12,<4.0
8
8
  Classifier: Programming Language :: Python :: 3
9
9
  Classifier: Programming Language :: Python :: 3.12
10
- Requires-Dist: fluent-validation (==4.3.1)
11
10
  Requires-Dist: mysql-connector-python (>=9.0.0,<10.0.0)
12
- Requires-Dist: python-dotenv (>=1.0.1,<2.0.0)
13
11
  Requires-Dist: shapely (>=2.0.6,<3.0.0)
14
12
  Description-Content-Type: text/markdown
15
13
 
@@ -17,7 +15,7 @@ Description-Content-Type: text/markdown
17
15
  ![downloads](https://img.shields.io/pypi/dm/ormlambda?label=downloads)
18
16
  ![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)
19
17
 
20
- # ormMySQL
18
+ # ormlambda Documentation
21
19
  This ORM is designed to connect with a MySQL server, facilitating the management of various database queries. Built with flexibility and efficiency in mind, this ORM empowers developers to interact with the database using lambda functions, allowing for concise and expressive query construction.
22
20
 
23
21
  # Creating your first lambda query
@@ -38,19 +36,34 @@ database = MySQLRepository(user=USERNAME, password=PASSWORD, database="sakila",
38
36
  ## Select all columns
39
37
  ```python
40
38
  from ormlambda import ORM
41
- from ormlambda.databases.my_sql import MySQLRepository
39
+ from ormlambda import create_engine
42
40
 
43
41
  from models.address import Address
44
- from config import config_dict
42
+ from test.config import config_dict
45
43
 
46
- db = MySQLRepository(**config_dict)
44
+ db = create_engine('mysql://root:1234@localhost:3306/sakila')
47
45
 
48
- AddressModel = ORM(Address,db)
46
+ AddressModel = ORM(Address, db)
49
47
 
50
48
  result = AddressModel.select()
51
49
  ```
52
50
  The `result` var will be of type `tuple[Address, ...]`
53
51
 
52
+ ## Improving Typing
53
+ For those cases where you need to pass the database configuration from a `dict`, you can use `MySQLArgs` TypedDict object to improve type annotations.
54
+
55
+ ```python
56
+ from ormlambda.databases.my_sql.types import MySQLArgs
57
+
58
+ config_dict: MySQLArgs = {
59
+ "user": DB_USERNAME,
60
+ "password": DB_PASSWORD,
61
+ "host": DB_HOST,
62
+ "database": DB_DATABASE,
63
+ }
64
+ db = MySQLRepository(**config_dict)
65
+ ```
66
+
54
67
  ## Select multiples tables
55
68
  Once the `AddressModel` class is created, we will not only be able to access all the information in that table, but also all the information in all the tables that have foreign keys related to it."
56
69
 
@@ -296,6 +309,92 @@ res = AddressModel.sum(Address.address_id, execute=True)
296
309
  res = AddressModel.count(Address.address_id, execute=True)
297
310
  ```
298
311
 
312
+ ## 1. Concat
313
+
314
+ The `concat` method allows you to concatenate multiple columns or values into a single string. This is particularly useful for creating derived fields in your queries.
315
+
316
+ #### Usage
317
+
318
+ ```python
319
+ response = ORM(Address, db).where(Address.City.Country.country.regex(r"^Spain")).first(
320
+ (
321
+ Address.address,
322
+ Address.City.city,
323
+ self.tmodel.concat(
324
+ (
325
+ "Address: ",
326
+ Address.address,
327
+ " - city: ",
328
+ Address.City.city,
329
+ " - country: ",
330
+ Address.City.Country.country,
331
+ )
332
+ ),
333
+ ),
334
+ flavour=dict,
335
+ )
336
+
337
+ {
338
+ "address_address": "939 Probolinggo Loop",
339
+ "city_city": "A Coruña (La Coruña)",
340
+ "CONCAT": "Address: 939 Probolinggo Loop - city: A Coruña (La Coruña) - country: Spain",
341
+ }
342
+ ```
343
+ As you can see in the response, the result is a dictionary where the keys are a combination of the table name and the column name. This is done to avoid collisions with columns from other tables that might have the same name.
344
+
345
+ Another elegant approach to adjust the response and obtain an object is by using the `flavour` attribute. You can pass a callable object, which will be used to instantiate it with the returned data.
346
+
347
+ ## Using BaseModel for Custom Responses (Pydantic)
348
+
349
+ You can utilize `BaseModel` from Pydantic to create structured response models. This allows you to define the expected structure of your data, ensuring type safety and validation.
350
+
351
+ ### Example: Creating a Custom Response Model
352
+
353
+ You can create a custom response model by subclassing `BaseModel`. In this model, you define the fields that you expect in your response, along with their types.
354
+
355
+ ```python
356
+ class AddressCombine(BaseModel):
357
+ address: str
358
+ city: str
359
+ country: str
360
+
361
+ model_config: ConfigDict = {"extra": "forbid"}
362
+
363
+ ddbb = MySQLRepository(**config_dict)
364
+ model = ORM(Address, ddbb)
365
+ select = (
366
+ model.order(lambda x: x.City.Country.country, "DESC")
367
+ .limit(10)
368
+ .where(Address.City.Country.country == "Spain")
369
+ .first(
370
+ lambda x: (
371
+ x.address,
372
+ x.City.city,
373
+ x.City.Country.country,
374
+ ),
375
+ flavour=AddressCombine,
376
+ )
377
+ )
378
+ ```
379
+
380
+ Once you execute the query, the result will be an instance of your custom model. You can access the fields directly, ensuring that the data adheres to the structure you defined.
381
+
382
+ ```python
383
+
384
+
385
+ print(select.address)
386
+ print(select.city)
387
+ print(select.country)
388
+ ```
389
+
390
+
391
+ <!-- ### 2. Having
392
+
393
+ The `having` method is used to filter results based on aggregate functions. It is typically used in conjunction with `group by` clauses.
394
+
395
+ #### Usage -->
396
+
397
+
299
398
  ## Combine aggregation method
300
399
  As shown in the previous examples, setting the `execute` attribute to `True` allows us to perform the corresponding query in a single line. However, if you're looking to improve efficiency, you can combine all of them into one query.
301
400
  ```python
@@ -1,4 +1,4 @@
1
- ormlambda/__init__.py,sha256=KGnAPj90RWKP1GnuSQD1rAaREXfE2pwzg7vCTYB36ZI,619
1
+ ormlambda/__init__.py,sha256=NKN-ggMFCOE4Zhir1cfFEf6c19hdPol_zWI6Fj5-h30,673
2
2
  ormlambda/caster/__init__.py,sha256=JWJ6qdPTk_uyJHGfFpvFCZeOXdP6DzL4-MtMFZVDPRY,150
3
3
  ormlambda/caster/base_caster.py,sha256=c3vCGoKDyJ39kfUiS7sNKhKdjBRYSK1Ie88lwDIXqgE,1774
4
4
  ormlambda/caster/caster.py,sha256=ecDvGgMI3yFtJxqKD9XcSVtBcqQtYitfMTooWPlLnEY,1885
@@ -38,7 +38,7 @@ ormlambda/components/upsert/abstract_upsert.py,sha256=a5_dJxTKI6GtxNTesxKtGFyy-A
38
38
  ormlambda/databases/__init__.py,sha256=-m7uIigkbFNU5JHeOE8DeTA2bRVYEppw2XPASTSvW_o,136
39
39
  ormlambda/databases/my_sql/__init__.py,sha256=2Ue97WtcpfB6-8SgUIHLOzk_iT44YUzG6baXCHmV9uA,212
40
40
  ormlambda/databases/my_sql/caster/__init__.py,sha256=Df2sdZaAJ1Mi5Ego0sILMk5pF1NbK-nlV0hbpzd0PWE,47
41
- ormlambda/databases/my_sql/caster/caster.py,sha256=D8hDwyZXOw5GRTQwONZyLaYe0sAimbDGb7DnDxUv_Ng,1181
41
+ ormlambda/databases/my_sql/caster/caster.py,sha256=6byP5RJsWmnLZ2C9Br8OpdpilLedogDlTv3i8HMObu8,1217
42
42
  ormlambda/databases/my_sql/caster/read.py,sha256=PBseYoNJtMgmr4sL5aD6uef0f-mm6LakAz-v-IcBs7w,1096
43
43
  ormlambda/databases/my_sql/caster/types/__init__.py,sha256=lZ9YFqDjY6d6-Vy0tBMZfLI5EhyKXrb12Ys4eYiWjAg,396
44
44
  ormlambda/databases/my_sql/caster/types/bytes.py,sha256=Aq4iO-JmHeCXLqcQfNqsSTUpc1gceqY3TC4H3PJfv3g,899
@@ -52,39 +52,43 @@ ormlambda/databases/my_sql/caster/types/string.py,sha256=uYIfXHLrXhiShd9KRALBc2U
52
52
  ormlambda/databases/my_sql/caster/write.py,sha256=mfbtlAFamsnMbe1L4ARSAw2ch5qhz15jh5cSyAljcss,1322
53
53
  ormlambda/databases/my_sql/clauses/ST_AsText.py,sha256=Fx-CgQ01aSkcuSlcdmLIWb7f3kd7r6kWs_BGu1HOVx8,1165
54
54
  ormlambda/databases/my_sql/clauses/ST_Contains.py,sha256=K9cZwQaQQCgZTQdnAokln5lVSyV99CkeRnmOd2RIots,968
55
- ormlambda/databases/my_sql/clauses/__init__.py,sha256=ZtIPgEoHXw5Dyv1U9aHnuKRhGQOPNhbtW1pNMUSfVvA,826
56
- ormlambda/databases/my_sql/clauses/alias.py,sha256=NLISh_c8L7ZwN_13mhmVUxvop6kU-j2N09GmuDjLRXs,1147
55
+ ormlambda/databases/my_sql/clauses/__init__.py,sha256=H1DDCYiqAhrxXgovUaju5tsgLv8kU6y8Lh6vZO9uHTM,863
56
+ ormlambda/databases/my_sql/clauses/alias.py,sha256=Xo0zQwyza3SMMRPIlBJWDQAJdW3bHjrC_UDps26P-QU,989
57
57
  ormlambda/databases/my_sql/clauses/count.py,sha256=rh7KNzpxkKEZpqmFV0oc8xHVgOHVGTGHrPGmCF-eLB4,1384
58
58
  ormlambda/databases/my_sql/clauses/create_database.py,sha256=zpd8uosxKJsf9BULvAHSd1-fU5de8OI7WRqVa8oyiA4,1209
59
59
  ormlambda/databases/my_sql/clauses/delete.py,sha256=Vm9lRKuX9x_m8faTlOs_A3v9zzoGGCnQ7CH_-QM6uK4,1900
60
60
  ormlambda/databases/my_sql/clauses/drop_database.py,sha256=2GYhtWzHSWM7Yy3v_l2hiY4fFumG8DSCGGLgP0t3Rhk,437
61
61
  ormlambda/databases/my_sql/clauses/drop_table.py,sha256=ltaJJFcNXChBF7fYGNCZK9QQ4iWfG72HFacL57f2k6A,569
62
- ormlambda/databases/my_sql/clauses/group_by.py,sha256=u5cYkWslAGr1-NTLn-OdKZQAjfZdwwi7WH2TX2qhTec,900
62
+ ormlambda/databases/my_sql/clauses/group_by.py,sha256=m-Q6GjhjAx5Y1hH3bGmWOIvdMSE_ilEY8YNCV7fqT5g,849
63
+ ormlambda/databases/my_sql/clauses/having.py,sha256=yfRJP3Zw4JEdQPkJtqQ4cZwxcUJTBLlb4e7nTRMaUSk,400
63
64
  ormlambda/databases/my_sql/clauses/insert.py,sha256=xR9IRiJeTIw-65MhnbK_uemMdvIEAaqFPg4IFBtC9Yk,3691
64
65
  ormlambda/databases/my_sql/clauses/joins.py,sha256=PEq7_qxgjJK5R_m8WkqJWppEc-bQFIbAb88qn81uGOg,5170
65
66
  ormlambda/databases/my_sql/clauses/limit.py,sha256=32Fii_WHjrX7K5B7H5uWlzYM6KBMFsE-Uz-70CEvTok,386
66
67
  ormlambda/databases/my_sql/clauses/offset.py,sha256=PKieZvCYLSSza-Nhcam5DJEYv--jBU8RHwju3P_f9Kk,390
67
- ormlambda/databases/my_sql/clauses/order.py,sha256=8sUKxWvkkOc5nn-r6GGL5MbFkCAD_St1CWMQkStSrLE,2147
68
+ ormlambda/databases/my_sql/clauses/order.py,sha256=n4vrssvUH9e8vlDHvML6Bclw6KHurkWePQ_WZavVxRE,2316
68
69
  ormlambda/databases/my_sql/clauses/select.py,sha256=O1dZWKG3_93-s_X_EjOF_alp_U82EzfDUKBMBcajxKY,1830
69
70
  ormlambda/databases/my_sql/clauses/update.py,sha256=GAXqEPEnUUO-M1zlaTU1Esso0s6lL7nmZWSEErZK2iE,2940
70
71
  ormlambda/databases/my_sql/clauses/upsert.py,sha256=VJAwfvpIT3xXwm3AnifVqiswnYNleDTnbY0CgnQqC0s,1945
71
- ormlambda/databases/my_sql/clauses/where.py,sha256=IR4BR4Rl13GiupCC3ilkUgqYu-LmISux7XZ6JIQGziw,1692
72
+ ormlambda/databases/my_sql/clauses/where.py,sha256=IIyXt98S_ExpC0IHqEO2OL1j-VK9An3Kkbsd2t-2OQU,1694
72
73
  ormlambda/databases/my_sql/functions/__init__.py,sha256=hA8t3mUpV2p-pO4TVp5rjC5Yp7aIkWPsS8NpLi3DUh0,171
73
- ormlambda/databases/my_sql/functions/concat.py,sha256=ih5An-1vxEVw0On0VLTuszVMku1c3eZmyUEdL4mO83o,1296
74
- ormlambda/databases/my_sql/functions/max.py,sha256=01kkgougyP5m9_wdZB9_QvHKV86GnfZdotwsTSwAQfA,1493
74
+ ormlambda/databases/my_sql/functions/concat.py,sha256=jGUQEj28Gh89ilwJqwFpP5mXyw9tvZ4iC2DrvS4X25M,1346
75
+ ormlambda/databases/my_sql/functions/max.py,sha256=AjROl4V9RYPMQoo0TomoTB8fRiMyN5-n6hTGRT_G-pY,1461
75
76
  ormlambda/databases/my_sql/functions/min.py,sha256=9g8twv0wUfpxrRK2socnfkJtmd_78pU6HDk_4AiV03c,1487
76
77
  ormlambda/databases/my_sql/functions/sum.py,sha256=PUItqZ4ZbBcOfPzGbYDVvhVKV1RxLMuI63xNbSD8KrA,1487
77
- ormlambda/databases/my_sql/join_context.py,sha256=Rx_FO9wOKeox6IZo8E-WNmgWbHxQ0qm049ZxiBPfcNY,3191
78
+ ormlambda/databases/my_sql/join_context.py,sha256=SLxVSwCURLOBSMB-aN7PV69sBQXOOP1sCezAKn3Wnqo,3256
78
79
  ormlambda/databases/my_sql/repository/__init__.py,sha256=_T7m-UpgJlx7eYdjBw8jdSVFGnjFYAcbp45g4EM7YEk,54
79
- ormlambda/databases/my_sql/repository/repository.py,sha256=jSD3RDI5-UZQcRoboDXCkfhi6_6wx5htwixG0Qmtm38,10661
80
- ormlambda/databases/my_sql/statements.py,sha256=4wPaihZe6vMRn5hhTkRjTjr-k95qRsCC5PqsXtjVt9I,16292
80
+ ormlambda/databases/my_sql/repository/repository.py,sha256=DXxY1I4NMA7zshWpzSc6TKf3RlNsOfdk_DDjCF7PoxQ,12149
81
+ ormlambda/databases/my_sql/statements.py,sha256=9mqKSVsgceRc666aG56CoJsUu5zI5LAg0CWmeRQK928,17280
81
82
  ormlambda/databases/my_sql/types.py,sha256=6c7LMS1VGR8ko1LB1T8DQzn1l10Mzk8PfIeYEOb9w30,1839
82
- ormlambda/engine/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
83
+ ormlambda/engine/__init__.py,sha256=tTA0VcN-kxpmnTS0smq7IK0ohGVwpM5TIju-sDnZhfk,82
84
+ ormlambda/engine/create.py,sha256=HwtLi9T_Z7mnZIPfSmutRPT7SlIDFfs82oJuVe-pObo,955
83
85
  ormlambda/engine/template.py,sha256=colmdl-IBSGc1eIqIYShsVkseQwF5_gwwc6Pt8ndN24,1350
86
+ ormlambda/engine/url.py,sha256=4FnkQQO93jQcGWmZF38DiJgIrRcIgIYacy2N6Dy3YX8,25401
87
+ ormlambda/engine/utils.py,sha256=fFoiKsiFuLcjcBVYNebVoYnMrEj3aZdoxEVSNfCY-GM,522
84
88
  ormlambda/model/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
85
89
  ormlambda/model/base_model.py,sha256=S_PYs5ZO-LTgiJSyAmGUtXwFKLb9VnM2Rqlq4pdhppY,1021
86
90
  ormlambda/repository/__init__.py,sha256=4KAhKn6vWV7bslewvGMNqbbbUnz1DLnH4yy-M5QNAQA,112
87
- ormlambda/repository/base_repository.py,sha256=tcuAOnFdaE8J9nZXMSVGFVTjKzG6qA700OJoGg4t-yU,473
91
+ ormlambda/repository/base_repository.py,sha256=5PeMcBMFGmdTlob-M2_iX1PJ1sNZa3mimx9fWgOuiTM,422
88
92
  ormlambda/repository/interfaces/IDatabaseConnection.py,sha256=pxczjx0b53yjjg5hvVDloMgUTFDahVC3HlJLQjo9_1w,283
89
93
  ormlambda/repository/interfaces/IRepositoryBase.py,sha256=24AFQ9ZBnFBQtdt3nkyFXkUbEzEsD2P7OS0FnFECJB8,1206
90
94
  ormlambda/repository/interfaces/__init__.py,sha256=t8Mn0aRZm8uF4MGaqjEANTTADCdOwNF0THZ_qebyzwo,126
@@ -94,10 +98,10 @@ ormlambda/sql/clause_info/clause_info.py,sha256=V4IQ6fuqf0Y-AS0qykQNcpOA2joib3FL
94
98
  ormlambda/sql/clause_info/clause_info_context.py,sha256=Y32p3x4mqcdNHbAowrKaN_ldnGn7zvaP1UdAkWWryhM,2852
95
99
  ormlambda/sql/clause_info/interface/IAggregate.py,sha256=P-QPaTMAMHVR5M9-ClmL8wsj0uNGG5xpxjuAwWnzKxA,216
96
100
  ormlambda/sql/clause_info/interface/__init__.py,sha256=7eEyHO7P_1DB63bP0ej5EjKkvb7IHwaGlSG58Hrv2X0,49
97
- ormlambda/sql/column.py,sha256=pRrsFkUZlilvnHWDbKPvUhwyV-1bhYxlcYB-Fa8D7_4,5242
101
+ ormlambda/sql/column.py,sha256=pFCvJKiL4On1VVX41IutGq9eC60xf6Dd8zpXPRMDdFE,5978
98
102
  ormlambda/sql/comparer.py,sha256=XTi4QT2ICtssCPsF8yrMwLDswnu7lF3MO217hAAY0t8,5334
99
103
  ormlambda/sql/dtypes.py,sha256=Ji4QOyKD0n9bKe7yXIIduy9uEX5BaXolQSIsx0NXYJY,7843
100
- ormlambda/sql/foreign_key.py,sha256=lO_OiVTmMsFQpfp-102XZgPSsYBoVCO74i6YVdQ89yk,4773
104
+ ormlambda/sql/foreign_key.py,sha256=lz5TiHmPFKdfL7ESwUt_MnUy2XYUTiOUiQtammoQTwQ,5530
101
105
  ormlambda/sql/interfaces/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
102
106
  ormlambda/sql/table/__init__.py,sha256=nAHXi63eDJeGGCf4f0A7W6x4Rh3FJ2dpfzxPEN6r3bc,62
103
107
  ormlambda/sql/table/fields.py,sha256=ovNR3bJ473aKW-2NhbKr0iJlVpgW06jurHLob2IyZU8,2116
@@ -105,14 +109,14 @@ ormlambda/sql/table/table_constructor.py,sha256=_sc1WnhnjchvIzxQZ85qZJFGZExlmhgK
105
109
  ormlambda/sql/types.py,sha256=z5FME0m9j7zSKlxS21cZxHRg0pyTfiJbq7VWZ6IT0kM,832
106
110
  ormlambda/statements/__init__.py,sha256=mFER-VoLf5L2BjdQhWMw6rVQi8kpr-qZzi1ZSWRPIIU,99
107
111
  ormlambda/statements/base_statement.py,sha256=WLjQrWw72NhBdNkP8uO1UP8hzfv-leyFjmMa4ofa-fk,5660
108
- ormlambda/statements/interfaces/IStatements.py,sha256=At_qfRdjewpzSLIntT6_oGKUkFOY8VtcHIse30LTpyg,11435
112
+ ormlambda/statements/interfaces/IStatements.py,sha256=GhaC7KpQJT1i0vAZjreIN91H6PiNWvhmDt77ZWHxdW4,11731
109
113
  ormlambda/statements/interfaces/__init__.py,sha256=a3RyTNVA7DwWMqvVi7gFYP4MArdU-RUYixJcxfc79HY,76
110
- ormlambda/statements/types.py,sha256=5HiOXjIm4aQzafB5Mq9b8OyBV2AyEH_P-kjQ8aQ8bIY,1743
114
+ ormlambda/statements/types.py,sha256=0AYnj6WCQcS-4fhsaK_yu2zQR3guPhPluq_sZxH1UhQ,1938
111
115
  ormlambda/utils/__init__.py,sha256=SEgDWkwbSrYRv0If92Ewq53DKnxxv5HqEAQbETd1C6Q,50
112
116
  ormlambda/utils/module_tree/__init__.py,sha256=LNQtqkwO1ul49Th3aHAIiyt0Wt899GmXCc44Uz1eDyY,53
113
117
  ormlambda/utils/module_tree/dfs_traversal.py,sha256=lSF03G63XtJFLp03ueAmsHMBvhUkjptDbK3IugXm8iU,1425
114
118
  ormlambda/utils/module_tree/dynamic_module.py,sha256=SJWpOC5oqASGjCXYHW0JwzEpcZ_DkxKLyK4SpIsMbaA,8700
115
- ormlambda-3.7.2.dist-info/LICENSE,sha256=xBprFw8GJLdHMOoUqDk0427EvjIcbEREvXXVFULuuXU,1080
116
- ormlambda-3.7.2.dist-info/METADATA,sha256=k5LnQJmw-8gweaFIOv8l4AvbW7dpqiH5hvT-WEAuxsY,9305
117
- ormlambda-3.7.2.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
118
- ormlambda-3.7.2.dist-info/RECORD,,
119
+ ormlambda-3.11.1.dist-info/LICENSE,sha256=xBprFw8GJLdHMOoUqDk0427EvjIcbEREvXXVFULuuXU,1080
120
+ ormlambda-3.11.1.dist-info/METADATA,sha256=heOegmWNeP8I61l3pjhm6Gchi494A0CTTGoX6HPtgnc,12450
121
+ ormlambda-3.11.1.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
122
+ ormlambda-3.11.1.dist-info/RECORD,,