ormlambda 3.7.2__py3-none-any.whl → 3.11.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.
@@ -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:
@@ -29,6 +29,8 @@ from ..types import (
29
29
  WhereTypes,
30
30
  )
31
31
 
32
+ type SelectCols[T, TProp] = Callable[[T], ColumnType[TProp]] | ColumnType[TProp]
33
+
32
34
 
33
35
  class IStatements[T: Table](ABC):
34
36
  @abstractmethod
@@ -132,7 +134,7 @@ class IStatements[T: Table](ABC):
132
134
  @abstractmethod
133
135
  def count(
134
136
  self,
135
- selection: Callable[[T], tuple] = lambda x: "*",
137
+ selection: Callable[[T], tuple] = ...,
136
138
  alias_clause="count",
137
139
  execute: bool = False,
138
140
  ) -> Optional[IStatements[T]]: ...
@@ -160,6 +162,16 @@ class IStatements[T: Table](ABC):
160
162
  @abstractmethod
161
163
  def where[LProp, RTable, RProp](self, conditions: WhereTypes[T, LProp, RTable, RProp] = None) -> IStatements[T]: ...
162
164
 
165
+ # endregion
166
+
167
+ # region having
168
+
169
+ @overload
170
+ def having[LProp, RTable, RProp](self, conditions: Callable[[T], WhereTypes[T, LProp, RTable, RProp]]) -> IStatements[T]: ...
171
+
172
+ @abstractmethod
173
+ def having[LProp, RTable, RProp](self, conditions: WhereTypes[T, LProp, RTable, RProp] = None) -> IStatements[T]: ...
174
+
163
175
  # endregion
164
176
  # region order
165
177
  @overload
@@ -172,14 +184,14 @@ class IStatements[T: Table](ABC):
172
184
  # endregion
173
185
  # region concat
174
186
  @overload
175
- def concat[P](self, selector: Callable[[T], tuple[P]]) -> IAggregate: ...
187
+ def concat(self, selector: Callable[[T], str], alias: str = "CONCAT") -> IAggregate: ...
176
188
 
177
189
  # endregion
178
190
  # region max
179
191
  @overload
180
192
  def max[TProp](
181
193
  self,
182
- column: Callable[[T], TProp],
194
+ column: SelectCols[T, TProp],
183
195
  alias: Optional[str] = ...,
184
196
  execute: bool = ...,
185
197
  ) -> TProp: ...
@@ -188,7 +200,7 @@ class IStatements[T: Table](ABC):
188
200
  @overload
189
201
  def min[TProp](
190
202
  self,
191
- column: Callable[[T], TProp],
203
+ column: SelectCols[T, TProp],
192
204
  alias: Optional[str] = ...,
193
205
  execute: bool = ...,
194
206
  ) -> TProp: ...
@@ -197,7 +209,7 @@ class IStatements[T: Table](ABC):
197
209
  @overload
198
210
  def sum[TProp](
199
211
  self,
200
- column: Callable[[T], TProp],
212
+ column: SelectCols[T, TProp],
201
213
  alias: Optional[str] = ...,
202
214
  execute: bool = ...,
203
215
  ) -> TProp: ...
@@ -208,6 +220,8 @@ class IStatements[T: Table](ABC):
208
220
  # endregion
209
221
  # region select
210
222
  type SelectorType[TOri, *T] = Callable[[TOri], tuple[*T]] | tuple[*T]
223
+ type SelectorFlavourType[T, TResponse] = Optional[Callable[[T], TResponse]] | TResponse
224
+ type SelectorOneType[T, TResponse] = Callable[[T, TResponse]] | TResponse
211
225
 
212
226
  @overload
213
227
  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 +248,6 @@ class IStatements[T: Table](ABC):
234
248
  @overload
235
249
  def select(self) -> Tuple[T]: ...
236
250
 
237
- type SelectorFlavourType[T, TResponse] = Optional[Callable[[T], TResponse]]
238
-
239
251
  # @overload
240
252
  # def select[TFlavour](self, selector: Optional[Callable[[T], tuple]] = ..., *, cast_to_tuple: bool = ..., flavour: Type[TFlavour], by: Optional[Enum] = ..., **kwargs) -> TFlavour: ...
241
253
  @overload
@@ -243,7 +255,7 @@ class IStatements[T: Table](ABC):
243
255
  @overload
244
256
  def select[*TRes](self, selector: SelectorFlavourType[T, tuple[*TRes]] = ..., *, flavour: Type[tuple], by: Optional[Enum] = ..., **kwargs) -> tuple[tuple[*TRes]]: ...
245
257
  @overload
246
- def select[TFlavour](self, selector: SelectorFlavourType[T, tuple] = ..., *, flavour: Type[TFlavour], by: Optional[Enum] = ..., **kwargs) -> tuple[TFlavour]: ...
258
+ def select[TFlavour](self, selector: SelectorFlavourType[T, tuple] = ..., *, flavour: Type[TFlavour], by: Optional[Enum] = ..., **kwargs) -> tuple[TFlavour, ...]: ...
247
259
 
248
260
  @abstractmethod
249
261
  def select[TValue, TFlavour, P](self, selector: SelectorFlavourType[T, tuple[TValue, P]] = ..., *, cast_to_tuple: bool = ..., flavour: Type[TFlavour] = ..., by: JoinType = ..., **kwargs): ...
@@ -255,21 +267,21 @@ class IStatements[T: Table](ABC):
255
267
  @overload
256
268
  def select_one[TFlavour](self, *, by: Optional[Enum] = ..., flavour: Type[TFlavour], **kwargs) -> TFlavour: ...
257
269
  @overload
258
- def select_one[T1](self, selector: Callable[[T], T1 | tuple[T1]], *, by: Optional[Enum] = ...) -> T1: ...
270
+ def select_one[T1](self, selector: SelectorOneType[T, T1 | tuple[T1]], *, by: Optional[Enum] = ...) -> T1: ...
259
271
  @overload
260
- def select_one[*TRes](self, selector: Callable[[T], tuple[*TRes]], *, by: Optional[Enum] = ...) -> tuple[*TRes]: ...
272
+ def select_one[*TRes](self, selector: SelectorOneType[T, tuple[*TRes]], *, by: Optional[Enum] = ...) -> tuple[*TRes]: ...
261
273
  @overload
262
- def select_one[T1](self, selector: Callable[[T], tuple[T1]], *, by: Optional[Enum] = ..., flavour: Type, **kwargs) -> T1: ...
274
+ def select_one[T1](self, selector: SelectorOneType[T, tuple[T1]], *, by: Optional[Enum] = ..., flavour: Type, **kwargs) -> T1: ...
263
275
  @overload
264
- def select_one[T1, TFlavour](self, selector: Callable[[T], T1], *, by: Optional[Enum] = ..., flavour: Type[TFlavour], **kwargs) -> TFlavour: ...
276
+ def select_one[T1, TFlavour](self, selector: SelectorOneType[T, T1], *, by: Optional[Enum] = ..., flavour: Type[TFlavour], **kwargs) -> TFlavour: ...
265
277
  @overload
266
- def select_one[*TRest](self, selector: Callable[[T], tuple[*TRest]], *, by: Optional[Enum] = ..., flavour: Type[tuple], **kwargs) -> tuple[*TRest]: ...
278
+ def select_one[*TRest](self, selector: SelectorOneType[T, tuple[*TRest]], *, by: Optional[Enum] = ..., flavour: Type[tuple], **kwargs) -> tuple[*TRest]: ...
267
279
  @overload
268
- def select_one[TFlavour](self, selector: Callable[[T], tuple], *, by: Optional[Enum] = ..., flavour: Type[TFlavour], **kwargs) -> TFlavour: ...
280
+ def select_one[TFlavour](self, selector: SelectorOneType[T, tuple], *, by: Optional[Enum] = ..., flavour: Type[TFlavour], **kwargs) -> TFlavour: ...
269
281
  @abstractmethod
270
282
  def select_one[TValue, TFlavour, *TRest](
271
283
  self,
272
- selector: Optional[Callable[[T], tuple[TValue, *TRest]]] = lambda: None,
284
+ selector: Optional[SelectorOneType[T, tuple[TValue, *TRest]]] = lambda: None,
273
285
  *,
274
286
  flavour: Type[TFlavour] = ...,
275
287
  by: Optional[Enum] = ...,
@@ -281,23 +293,23 @@ class IStatements[T: Table](ABC):
281
293
  @overload
282
294
  def first(self) -> T: ...
283
295
  @overload
284
- def first[TFlavour](self, *, by: Optional[Enum] = ..., flavour: Type[TFlavour], **kwargs) -> TFlavour: ...
296
+ def first[T1](self, selector: SelectorOneType[T, T1 | tuple[T1]], *, by: Optional[Enum] = ...) -> T1: ...
285
297
  @overload
286
- def first[T1](self, selector: Callable[[T], T1 | tuple[T1]], *, by: Optional[Enum] = ...) -> T1: ...
298
+ def first[*TRes](self, selector: SelectorOneType[T, tuple[*TRes]], *, by: Optional[Enum] = ...) -> tuple[*TRes]: ...
287
299
  @overload
288
- def first[*TRes](self, selector: Callable[[T], tuple[*TRes]], *, by: Optional[Enum] = ...) -> tuple[*TRes]: ...
300
+ def first[TFlavour](self, *, by: Optional[Enum] = ..., flavour: Type[TFlavour], **kwargs) -> TFlavour: ...
289
301
  @overload
290
- def first[T1](self, selector: Callable[[T], tuple[T1]], *, by: Optional[Enum] = ..., flavour: Type, **kwargs) -> T1: ...
302
+ def first[T1](self, selector: SelectorOneType[T, tuple[T1]], *, by: Optional[Enum] = ..., flavour: Type, **kwargs) -> T1: ...
291
303
  @overload
292
- def first[T1, TFlavour](self, selector: Callable[[T], T1], *, by: Optional[Enum] = ..., flavour: Type[TFlavour], **kwargs) -> TFlavour: ...
304
+ def first[T1, TFlavour](self, selector: SelectorOneType[T, T1], *, by: Optional[Enum] = ..., flavour: Type[TFlavour], **kwargs) -> TFlavour: ...
293
305
  @overload
294
- def first[*TRest](self, selector: Callable[[T], tuple[*TRest]], *, by: Optional[Enum] = ..., flavour: Type[tuple], **kwargs) -> tuple[*TRest]: ...
306
+ def first[*TRest](self, selector: SelectorOneType[T, tuple[*TRest]], *, by: Optional[Enum] = ..., flavour: Type[tuple], **kwargs) -> tuple[*TRest]: ...
295
307
  @overload
296
- def first[TFlavour](self, selector: Callable[[T], tuple], *, by: Optional[Enum] = ..., flavour: Type[TFlavour], **kwargs) -> TFlavour: ...
308
+ def first[TFlavour](self, selector: SelectorOneType[T, tuple], *, by: Optional[Enum] = ..., flavour: Type[TFlavour], **kwargs) -> TFlavour: ...
297
309
  @abstractmethod
298
310
  def first[TValue, TFlavour, *TRest](
299
311
  self,
300
- selector: Optional[Callable[[T], tuple[TValue, *TRest]]] = lambda: None,
312
+ selector: Optional[SelectorOneType[T, tuple[TValue, *TRest]]] = lambda: None,
301
313
  *,
302
314
  flavour: Type[TFlavour] = ...,
303
315
  by: Optional[Enum] = ...,
@@ -305,9 +317,9 @@ class IStatements[T: Table](ABC):
305
317
 
306
318
  # endregion
307
319
 
308
- # region group_by
320
+ # region groupby
309
321
  @abstractmethod
310
- def group_by[TRepo](self, column: Callable[[T], TRepo]) -> IStatements[T]: ...
322
+ def groupby[TRepo](self, column: Callable[[T], TRepo]) -> IStatements[T]: ...
311
323
 
312
324
  # endregion
313
325
 
@@ -18,7 +18,10 @@ if TYPE_CHECKING:
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
 
@@ -1,15 +1,13 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ormlambda
3
- Version: 3.7.2
3
+ Version: 3.11.0
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/concat.py,sha256=jGUQEj28Gh89ilwJqwFpP5mXyw9tvZ4iC2DrvS4X25M,1346
74
75
  ormlambda/databases/my_sql/functions/max.py,sha256=01kkgougyP5m9_wdZB9_QvHKV86GnfZdotwsTSwAQfA,1493
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=CVBUmzVWC91XW-TBsiH1640JPeaLVMdwOHcDWCB49tQ,17308
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=eWSuNvV9vDlZh_5qFHcVq6GGLpvBzEiboXxRCpLSRTE,12010
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=CYoWoopGbhg-_1GRkBZYMYpWzwciHw5Kbt-4vzGlclI,1805
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.0.dist-info/LICENSE,sha256=xBprFw8GJLdHMOoUqDk0427EvjIcbEREvXXVFULuuXU,1080
120
+ ormlambda-3.11.0.dist-info/METADATA,sha256=-BL6Em1Z3NajxNSMbWmmQ8-u_h-hUlygGiiZS_LmzWw,12450
121
+ ormlambda-3.11.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
122
+ ormlambda-3.11.0.dist-info/RECORD,,