ormlambda 3.11.1__tar.gz → 3.12.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ormlambda-3.11.1/README.md → ormlambda-3.12.2/PKG-INFO +78 -15
- ormlambda-3.11.1/PKG-INFO → ormlambda-3.12.2/README.md +64 -28
- {ormlambda-3.11.1 → ormlambda-3.12.2}/pyproject.toml +1 -1
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/__init__.py +1 -1
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/caster/caster.py +1 -1
- ormlambda-3.12.2/src/ormlambda/common/abstract_classes/clause_info_converter.py +73 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/common/abstract_classes/decomposition_query.py +12 -68
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/common/abstract_classes/non_query_base.py +2 -2
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/common/interfaces/ICustomAlias.py +1 -1
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/components/delete/abstract_delete.py +2 -2
- ormlambda-3.12.2/src/ormlambda/components/join/__init__.py +1 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/databases/my_sql/clauses/delete.py +0 -1
- ormlambda-3.12.2/src/ormlambda/databases/my_sql/clauses/drop_table.py +26 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/databases/my_sql/clauses/group_by.py +1 -2
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/databases/my_sql/clauses/select.py +2 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/databases/my_sql/clauses/upsert.py +8 -4
- ormlambda-3.12.2/src/ormlambda/databases/my_sql/query_builder.py +158 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/databases/my_sql/statements.py +19 -156
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/engine/__init__.py +1 -1
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/engine/url.py +4 -1
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/sql/clause_info/__init__.py +2 -1
- ormlambda-3.12.2/src/ormlambda/sql/clause_info/aggregate_function_base.py +86 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/sql/clause_info/clause_info.py +1 -98
- ormlambda-3.12.2/src/ormlambda/sql/clause_info/interface/IClauseInfo.py +37 -0
- ormlambda-3.12.2/src/ormlambda/sql/clause_info/interface/__init__.py +2 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/sql/column.py +3 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/statements/base_statement.py +6 -2
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/statements/interfaces/IStatements.py +25 -19
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/utils/module_tree/dynamic_module.py +3 -2
- ormlambda-3.11.1/src/ormlambda/databases/my_sql/caster/read.py +0 -39
- ormlambda-3.11.1/src/ormlambda/databases/my_sql/caster/write.py +0 -37
- ormlambda-3.11.1/src/ormlambda/databases/my_sql/clauses/drop_table.py +0 -23
- ormlambda-3.11.1/src/ormlambda/sql/clause_info/interface/__init__.py +0 -1
- {ormlambda-3.11.1 → ormlambda-3.12.2}/LICENSE +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/caster/__init__.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/caster/base_caster.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/caster/interfaces/ICaster.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/caster/interfaces/__init__.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/common/__init__.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/common/abstract_classes/__init__.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/common/abstract_classes/query_base.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/common/enums/__init__.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/common/enums/condition_types.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/common/enums/join_type.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/common/errors/__init__.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/common/global_checker.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/common/interfaces/IDecompositionQuery.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/common/interfaces/IJoinSelector.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/common/interfaces/INonQueryCommand.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/common/interfaces/IQueryCommand.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/common/interfaces/__init__.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/components/__init__.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/components/delete/IDelete.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/components/delete/__init__.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/components/insert/IInsert.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/components/insert/__init__.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/components/insert/abstract_insert.py +0 -0
- {ormlambda-3.11.1/src/ormlambda/databases/my_sql → ormlambda-3.12.2/src/ormlambda/components/join}/join_context.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/components/select/ISelect.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/components/select/__init__.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/components/update/IUpdate.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/components/update/__init__.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/components/update/abstract_update.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/components/upsert/IUpsert.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/components/upsert/__init__.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/components/upsert/abstract_upsert.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/databases/__init__.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/databases/my_sql/__init__.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/databases/my_sql/caster/__init__.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/databases/my_sql/caster/caster.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/databases/my_sql/caster/types/__init__.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/databases/my_sql/caster/types/bytes.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/databases/my_sql/caster/types/datetime.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/databases/my_sql/caster/types/float.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/databases/my_sql/caster/types/int.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/databases/my_sql/caster/types/iterable.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/databases/my_sql/caster/types/none.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/databases/my_sql/caster/types/point.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/databases/my_sql/caster/types/string.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/databases/my_sql/clauses/ST_AsText.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/databases/my_sql/clauses/ST_Contains.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/databases/my_sql/clauses/__init__.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/databases/my_sql/clauses/alias.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/databases/my_sql/clauses/count.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/databases/my_sql/clauses/create_database.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/databases/my_sql/clauses/drop_database.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/databases/my_sql/clauses/having.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/databases/my_sql/clauses/insert.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/databases/my_sql/clauses/joins.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/databases/my_sql/clauses/limit.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/databases/my_sql/clauses/offset.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/databases/my_sql/clauses/order.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/databases/my_sql/clauses/update.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/databases/my_sql/clauses/where.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/databases/my_sql/functions/__init__.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/databases/my_sql/functions/concat.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/databases/my_sql/functions/max.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/databases/my_sql/functions/min.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/databases/my_sql/functions/sum.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/databases/my_sql/repository/__init__.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/databases/my_sql/repository/repository.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/databases/my_sql/types.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/engine/create.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/engine/template.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/engine/utils.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/model/__init__.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/model/base_model.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/repository/__init__.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/repository/base_repository.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/repository/interfaces/IDatabaseConnection.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/repository/interfaces/IRepositoryBase.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/repository/interfaces/__init__.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/sql/__init__.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/sql/clause_info/clause_info_context.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/sql/clause_info/interface/IAggregate.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/sql/comparer.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/sql/dtypes.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/sql/foreign_key.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/sql/interfaces/__init__.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/sql/table/__init__.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/sql/table/fields.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/sql/table/table_constructor.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/sql/types.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/statements/__init__.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/statements/interfaces/__init__.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/statements/types.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/utils/__init__.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/utils/module_tree/__init__.py +0 -0
- {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/utils/module_tree/dfs_traversal.py +0 -0
@@ -1,3 +1,17 @@
|
|
1
|
+
Metadata-Version: 2.3
|
2
|
+
Name: ormlambda
|
3
|
+
Version: 3.12.2
|
4
|
+
Summary: ORM designed to interact with the database (currently with MySQL) using lambda functions and nested functions
|
5
|
+
Author: p-hzamora
|
6
|
+
Author-email: p.hzamora@icloud.com
|
7
|
+
Requires-Python: >=3.12,<4.0
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
9
|
+
Classifier: Programming Language :: Python :: 3.12
|
10
|
+
Classifier: Programming Language :: Python :: 3.13
|
11
|
+
Requires-Dist: mysql-connector-python (>=9.0.0,<10.0.0)
|
12
|
+
Requires-Dist: shapely (>=2.0.6,<3.0.0)
|
13
|
+
Description-Content-Type: text/markdown
|
14
|
+
|
1
15
|

|
2
16
|

|
3
17
|

|
@@ -56,10 +70,12 @@ Once the `AddressModel` class is created, we will not only be able to access all
|
|
56
70
|
|
57
71
|
```python
|
58
72
|
from models.address import Address
|
73
|
+
from ormlambda import create_engine, ORM
|
59
74
|
|
60
|
-
db =
|
75
|
+
db = create_engine('mysql://root:1234@localhost:3306/sakila')
|
61
76
|
|
62
|
-
|
77
|
+
|
78
|
+
AddressModel = ORM(Address, db)
|
63
79
|
|
64
80
|
result = AddressModel.where(Address.City.Country.country.regex(r"^[aA]")).select(
|
65
81
|
lambda address: (
|
@@ -300,7 +316,7 @@ res = AddressModel.count(Address.address_id, execute=True)
|
|
300
316
|
|
301
317
|
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.
|
302
318
|
|
303
|
-
|
319
|
+
### Usage
|
304
320
|
|
305
321
|
```python
|
306
322
|
response = ORM(Address, db).where(Address.City.Country.country.regex(r"^Spain")).first(
|
@@ -331,6 +347,60 @@ As you can see in the response, the result is a dictionary where the keys are a
|
|
331
347
|
|
332
348
|
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.
|
333
349
|
|
350
|
+
|
351
|
+
## 2. Group by
|
352
|
+
|
353
|
+
The `groupby` method is used to filter results based on aggregate functions.
|
354
|
+
|
355
|
+
### Usage
|
356
|
+
```python
|
357
|
+
from ormlambda import Column, ORM, create_engine
|
358
|
+
from test.config import DATABASE_URL
|
359
|
+
|
360
|
+
|
361
|
+
class Response(BaseModel):
|
362
|
+
district: str
|
363
|
+
count: int
|
364
|
+
|
365
|
+
engine= create_engine(DATABASE_URL)
|
366
|
+
model = ORM(Address,engine)
|
367
|
+
|
368
|
+
count_name = Column(column_name="count")
|
369
|
+
|
370
|
+
res = (
|
371
|
+
self.model
|
372
|
+
.groupby(Address.district)
|
373
|
+
.select(
|
374
|
+
(
|
375
|
+
Address.district,
|
376
|
+
self.model.count(Address.address),
|
377
|
+
),
|
378
|
+
flavour=Response,
|
379
|
+
)
|
380
|
+
)
|
381
|
+
```
|
382
|
+
|
383
|
+
## 3. Having
|
384
|
+
|
385
|
+
The `having` method is used to filter results based on aggregate functions. It is typically used in conjunction with `group by` clauses.
|
386
|
+
|
387
|
+
### Usage
|
388
|
+
```python
|
389
|
+
res = (
|
390
|
+
model
|
391
|
+
.groupby(Address.district)
|
392
|
+
.having(count_name > 4)
|
393
|
+
.select(
|
394
|
+
(
|
395
|
+
Address.district,
|
396
|
+
model.count(Address.address),
|
397
|
+
),
|
398
|
+
flavour=Response,
|
399
|
+
)
|
400
|
+
)
|
401
|
+
```
|
402
|
+
|
403
|
+
|
334
404
|
## Using BaseModel for Custom Responses (Pydantic)
|
335
405
|
|
336
406
|
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.
|
@@ -347,10 +417,11 @@ class AddressCombine(BaseModel):
|
|
347
417
|
|
348
418
|
model_config: ConfigDict = {"extra": "forbid"}
|
349
419
|
|
350
|
-
|
351
|
-
|
420
|
+
db = create_engine('mysql://root:1234@localhost:3306/sakila')
|
421
|
+
|
352
422
|
select = (
|
353
|
-
|
423
|
+
ORM(Address, db)
|
424
|
+
.order(lambda x: x.City.Country.country, "DESC")
|
354
425
|
.limit(10)
|
355
426
|
.where(Address.City.Country.country == "Spain")
|
356
427
|
.first(
|
@@ -374,14 +445,6 @@ print(select.city)
|
|
374
445
|
print(select.country)
|
375
446
|
```
|
376
447
|
|
377
|
-
|
378
|
-
<!-- ### 2. Having
|
379
|
-
|
380
|
-
The `having` method is used to filter results based on aggregate functions. It is typically used in conjunction with `group by` clauses.
|
381
|
-
|
382
|
-
#### Usage -->
|
383
|
-
|
384
|
-
|
385
448
|
## Combine aggregation method
|
386
449
|
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.
|
387
450
|
```python
|
@@ -420,4 +483,4 @@ AddressModel.select_one(
|
|
420
483
|
# "custom-max": 605,
|
421
484
|
# "count": 603,
|
422
485
|
# }
|
423
|
-
```
|
486
|
+
```
|
@@ -1,16 +1,3 @@
|
|
1
|
-
Metadata-Version: 2.1
|
2
|
-
Name: ormlambda
|
3
|
-
Version: 3.11.1
|
4
|
-
Summary: ORM designed to interact with the database (currently with MySQL) using lambda functions and nested functions
|
5
|
-
Author: p-hzamora
|
6
|
-
Author-email: p.hzamora@icloud.com
|
7
|
-
Requires-Python: >=3.12,<4.0
|
8
|
-
Classifier: Programming Language :: Python :: 3
|
9
|
-
Classifier: Programming Language :: Python :: 3.12
|
10
|
-
Requires-Dist: mysql-connector-python (>=9.0.0,<10.0.0)
|
11
|
-
Requires-Dist: shapely (>=2.0.6,<3.0.0)
|
12
|
-
Description-Content-Type: text/markdown
|
13
|
-
|
14
1
|

|
15
2
|

|
16
3
|

|
@@ -69,10 +56,12 @@ Once the `AddressModel` class is created, we will not only be able to access all
|
|
69
56
|
|
70
57
|
```python
|
71
58
|
from models.address import Address
|
59
|
+
from ormlambda import create_engine, ORM
|
72
60
|
|
73
|
-
db =
|
61
|
+
db = create_engine('mysql://root:1234@localhost:3306/sakila')
|
74
62
|
|
75
|
-
|
63
|
+
|
64
|
+
AddressModel = ORM(Address, db)
|
76
65
|
|
77
66
|
result = AddressModel.where(Address.City.Country.country.regex(r"^[aA]")).select(
|
78
67
|
lambda address: (
|
@@ -313,7 +302,7 @@ res = AddressModel.count(Address.address_id, execute=True)
|
|
313
302
|
|
314
303
|
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
304
|
|
316
|
-
|
305
|
+
### Usage
|
317
306
|
|
318
307
|
```python
|
319
308
|
response = ORM(Address, db).where(Address.City.Country.country.regex(r"^Spain")).first(
|
@@ -344,6 +333,60 @@ As you can see in the response, the result is a dictionary where the keys are a
|
|
344
333
|
|
345
334
|
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
335
|
|
336
|
+
|
337
|
+
## 2. Group by
|
338
|
+
|
339
|
+
The `groupby` method is used to filter results based on aggregate functions.
|
340
|
+
|
341
|
+
### Usage
|
342
|
+
```python
|
343
|
+
from ormlambda import Column, ORM, create_engine
|
344
|
+
from test.config import DATABASE_URL
|
345
|
+
|
346
|
+
|
347
|
+
class Response(BaseModel):
|
348
|
+
district: str
|
349
|
+
count: int
|
350
|
+
|
351
|
+
engine= create_engine(DATABASE_URL)
|
352
|
+
model = ORM(Address,engine)
|
353
|
+
|
354
|
+
count_name = Column(column_name="count")
|
355
|
+
|
356
|
+
res = (
|
357
|
+
self.model
|
358
|
+
.groupby(Address.district)
|
359
|
+
.select(
|
360
|
+
(
|
361
|
+
Address.district,
|
362
|
+
self.model.count(Address.address),
|
363
|
+
),
|
364
|
+
flavour=Response,
|
365
|
+
)
|
366
|
+
)
|
367
|
+
```
|
368
|
+
|
369
|
+
## 3. Having
|
370
|
+
|
371
|
+
The `having` method is used to filter results based on aggregate functions. It is typically used in conjunction with `group by` clauses.
|
372
|
+
|
373
|
+
### Usage
|
374
|
+
```python
|
375
|
+
res = (
|
376
|
+
model
|
377
|
+
.groupby(Address.district)
|
378
|
+
.having(count_name > 4)
|
379
|
+
.select(
|
380
|
+
(
|
381
|
+
Address.district,
|
382
|
+
model.count(Address.address),
|
383
|
+
),
|
384
|
+
flavour=Response,
|
385
|
+
)
|
386
|
+
)
|
387
|
+
```
|
388
|
+
|
389
|
+
|
347
390
|
## Using BaseModel for Custom Responses (Pydantic)
|
348
391
|
|
349
392
|
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.
|
@@ -360,10 +403,11 @@ class AddressCombine(BaseModel):
|
|
360
403
|
|
361
404
|
model_config: ConfigDict = {"extra": "forbid"}
|
362
405
|
|
363
|
-
|
364
|
-
|
406
|
+
db = create_engine('mysql://root:1234@localhost:3306/sakila')
|
407
|
+
|
365
408
|
select = (
|
366
|
-
|
409
|
+
ORM(Address, db)
|
410
|
+
.order(lambda x: x.City.Country.country, "DESC")
|
367
411
|
.limit(10)
|
368
412
|
.where(Address.City.Country.country == "Spain")
|
369
413
|
.first(
|
@@ -387,14 +431,6 @@ print(select.city)
|
|
387
431
|
print(select.country)
|
388
432
|
```
|
389
433
|
|
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
|
-
|
398
434
|
## Combine aggregation method
|
399
435
|
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.
|
400
436
|
```python
|
@@ -433,4 +469,4 @@ AddressModel.select_one(
|
|
433
469
|
# "custom-max": 605,
|
434
470
|
# "count": 603,
|
435
471
|
# }
|
436
|
-
```
|
472
|
+
```
|
@@ -3,7 +3,7 @@ line-length = 320
|
|
3
3
|
|
4
4
|
[tool.poetry]
|
5
5
|
name = "ormlambda"
|
6
|
-
version = "3.
|
6
|
+
version = "3.12.2"
|
7
7
|
description = "ORM designed to interact with the database (currently with MySQL) using lambda functions and nested functions"
|
8
8
|
authors = ["p-hzamora <p.hzamora@icloud.com>"]
|
9
9
|
readme = "README.md"
|
@@ -21,4 +21,4 @@ from .model.base_model import (
|
|
21
21
|
ORM as ORM,
|
22
22
|
) # COMMENT: to avoid relative import we need to import BaseModel after import Table,Column, ForeignKey, IRepositoryBase and Disassembler
|
23
23
|
|
24
|
-
from .engine import create_engine, URL # noqa: F401
|
24
|
+
from .engine import create_engine, URL, make_url # noqa: F401
|
@@ -0,0 +1,73 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
import typing as tp
|
3
|
+
from ormlambda import Table
|
4
|
+
|
5
|
+
from ormlambda.sql.clause_info import ClauseInfo, AggregateFunctionBase
|
6
|
+
from ormlambda.sql.clause_info.clause_info_context import ClauseContextType
|
7
|
+
from ormlambda import ForeignKey
|
8
|
+
|
9
|
+
from ormlambda.sql.types import AliasType, TableType, ColumnType
|
10
|
+
|
11
|
+
|
12
|
+
class ClauseInfoConverter[T, TProp](tp.Protocol):
|
13
|
+
@classmethod
|
14
|
+
def convert(cls, data: T, alias_table: AliasType[ColumnType[TProp]] = "{table}", context: ClauseContextType = None, **kwargs) -> list[ClauseInfo[T]]: ...
|
15
|
+
|
16
|
+
|
17
|
+
class ConvertFromAnyType(ClauseInfoConverter[None, None]):
|
18
|
+
@classmethod
|
19
|
+
def convert(cls, data: tp.Any, alias_table: AliasType = "{table}", context: ClauseContextType = None, **kwargs) -> list[ClauseInfo[None]]:
|
20
|
+
return [
|
21
|
+
ClauseInfo[None](
|
22
|
+
table=None,
|
23
|
+
column=data,
|
24
|
+
alias_table=alias_table,
|
25
|
+
alias_clause=kwargs.get("alias", None),
|
26
|
+
context=context,
|
27
|
+
)
|
28
|
+
]
|
29
|
+
|
30
|
+
|
31
|
+
class ConvertFromForeignKey[LT: Table, RT: Table](ClauseInfoConverter[RT, None]):
|
32
|
+
@classmethod
|
33
|
+
def convert(cls, data: ForeignKey[LT, RT], alias_table=None, context: ClauseContextType = None, **kwargs) -> list[ClauseInfo[RT]]:
|
34
|
+
return ConvertFromTable[RT].convert(data.tright, data.alias, context, **kwargs)
|
35
|
+
|
36
|
+
|
37
|
+
class ConvertFromColumn[TProp](ClauseInfoConverter[None, TProp]):
|
38
|
+
@classmethod
|
39
|
+
def convert(cls, data: ColumnType[TProp], alias_table: AliasType[ColumnType[TProp]] = "{table}", context: ClauseContextType = None, **kwargs) -> list[ClauseInfo[None]]:
|
40
|
+
# COMMENT: if the property belongs to the main class, the columnn name in not prefixed. This only done if the property comes from any join.
|
41
|
+
attributes = {
|
42
|
+
"table": data.table,
|
43
|
+
"column": data,
|
44
|
+
"alias_table": alias_table,
|
45
|
+
"alias_clause": "{table}_{column}",
|
46
|
+
"context": context,
|
47
|
+
**kwargs,
|
48
|
+
}
|
49
|
+
clause_info = ClauseInfo[None](**attributes)
|
50
|
+
return [clause_info]
|
51
|
+
|
52
|
+
|
53
|
+
class ConvertFromIAggregate(ClauseInfoConverter[None, None]):
|
54
|
+
@classmethod
|
55
|
+
def convert(cls, data: AggregateFunctionBase, alias_table=None, context: ClauseContextType = None, **kwargs) -> list[ClauseInfo[None]]:
|
56
|
+
return [data]
|
57
|
+
|
58
|
+
|
59
|
+
class ConvertFromTable[T: Table](ClauseInfoConverter[T, None]):
|
60
|
+
@classmethod
|
61
|
+
def convert(cls, data: T, alias_table: AliasType[ColumnType] = "{table}", context: ClauseContextType = None, **kwargs) -> list[ClauseInfo[T]]:
|
62
|
+
"""
|
63
|
+
if the data is Table, means that we want to retrieve all columns
|
64
|
+
"""
|
65
|
+
return cls._extract_all_clauses(data, alias_table, context, **kwargs)
|
66
|
+
|
67
|
+
@staticmethod
|
68
|
+
def _extract_all_clauses(table: TableType[T], alias_table: AliasType[ColumnType], context: ClauseContextType = None, **kwargs) -> list[ClauseInfo[TableType[T]]]:
|
69
|
+
# all columns
|
70
|
+
column_clauses = []
|
71
|
+
for column in table.get_columns():
|
72
|
+
column_clauses.extend(ConvertFromColumn.convert(column, alias_table=alias_table, context=context, **kwargs))
|
73
|
+
return column_clauses
|
{ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/common/abstract_classes/decomposition_query.py
RENAMED
@@ -1,6 +1,5 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
import typing as tp
|
3
|
-
import abc
|
4
3
|
from ormlambda import Table, Column
|
5
4
|
|
6
5
|
from ormlambda.common.interfaces import IDecompositionQuery, ICustomAlias
|
@@ -10,8 +9,15 @@ from ormlambda.sql.clause_info.clause_info_context import ClauseInfoContext, Cla
|
|
10
9
|
from ormlambda import ForeignKey
|
11
10
|
from ormlambda.common.global_checker import GlobalChecker
|
12
11
|
|
13
|
-
from ormlambda.sql.types import
|
14
|
-
|
12
|
+
from ormlambda.sql.types import TableType, ColumnType
|
13
|
+
from .clause_info_converter import (
|
14
|
+
ClauseInfoConverter,
|
15
|
+
ConvertFromAnyType,
|
16
|
+
ConvertFromForeignKey,
|
17
|
+
ConvertFromColumn,
|
18
|
+
ConvertFromIAggregate,
|
19
|
+
ConvertFromTable,
|
20
|
+
)
|
15
21
|
|
16
22
|
type TableTupleType[T, *Ts] = tuple[T:TableType, *Ts]
|
17
23
|
type ValueType = tp.Union[
|
@@ -22,69 +28,6 @@ type ValueType = tp.Union[
|
|
22
28
|
]
|
23
29
|
|
24
30
|
|
25
|
-
class ClauseInfoConverter[T, TProp](abc.ABC):
|
26
|
-
@classmethod
|
27
|
-
@abc.abstractmethod
|
28
|
-
def convert(cls, data: T, alias_table: AliasType[ColumnType[TProp]] = "{table}", context: ClauseContextType = None) -> list[ClauseInfo[T]]: ...
|
29
|
-
|
30
|
-
|
31
|
-
class ConvertFromAnyType(ClauseInfoConverter[None, None]):
|
32
|
-
@classmethod
|
33
|
-
def convert(cls, data: tp.Any, alias_table: AliasType = "{table}", context: ClauseContextType = None) -> list[ClauseInfo[None]]:
|
34
|
-
return [
|
35
|
-
ClauseInfo[None](
|
36
|
-
table=None,
|
37
|
-
column=data,
|
38
|
-
alias_table=alias_table,
|
39
|
-
alias_clause=None,
|
40
|
-
context=context,
|
41
|
-
)
|
42
|
-
]
|
43
|
-
|
44
|
-
|
45
|
-
class ConvertFromForeignKey[LT: Table, RT: Table](ClauseInfoConverter[RT, None]):
|
46
|
-
@classmethod
|
47
|
-
def convert(cls, data: ForeignKey[LT, RT], alias_table=None, context: ClauseContextType = None) -> list[ClauseInfo[RT]]:
|
48
|
-
return ConvertFromTable[RT].convert(data.tright, data.alias, context)
|
49
|
-
|
50
|
-
|
51
|
-
class ConvertFromColumn[TProp](ClauseInfoConverter[None, TProp]):
|
52
|
-
@classmethod
|
53
|
-
def convert(cls, data: ColumnType[TProp], alias_table: AliasType[ColumnType[TProp]] = "{table}", context: ClauseContextType = None) -> list[ClauseInfo[None]]:
|
54
|
-
# COMMENT: if the property belongs to the main class, the columnn name in not prefixed. This only done if the property comes from any join.
|
55
|
-
clause_info = ClauseInfo[None](
|
56
|
-
table=data.table,
|
57
|
-
column=data,
|
58
|
-
alias_table=alias_table,
|
59
|
-
alias_clause="{table}_{column}",
|
60
|
-
context=context,
|
61
|
-
)
|
62
|
-
return [clause_info]
|
63
|
-
|
64
|
-
|
65
|
-
class ConvertFromIAggregate(ClauseInfoConverter[None, None]):
|
66
|
-
@classmethod
|
67
|
-
def convert(cls, data: AggregateFunctionBase, alias_table=None, context: ClauseContextType = None) -> list[ClauseInfo[None]]:
|
68
|
-
return [data]
|
69
|
-
|
70
|
-
|
71
|
-
class ConvertFromTable[T: Table](ClauseInfoConverter[T, None]):
|
72
|
-
@classmethod
|
73
|
-
def convert(cls, data: T, alias_table: AliasType[ColumnType] = "{table}", context: ClauseContextType = None) -> list[ClauseInfo[T]]:
|
74
|
-
"""
|
75
|
-
if the data is Table, means that we want to retrieve all columns
|
76
|
-
"""
|
77
|
-
return cls._extract_all_clauses(data, alias_table, context)
|
78
|
-
|
79
|
-
@staticmethod
|
80
|
-
def _extract_all_clauses(table: TableType[T], alias_table: AliasType[ColumnType], context: ClauseContextType = None) -> list[ClauseInfo[TableType[T]]]:
|
81
|
-
# all columns
|
82
|
-
column_clauses = []
|
83
|
-
for column in table.get_columns():
|
84
|
-
column_clauses.extend(ConvertFromColumn.convert(column, alias_table=alias_table, context=context))
|
85
|
-
return column_clauses
|
86
|
-
|
87
|
-
|
88
31
|
class DecompositionQueryBase[T: Table, *Ts](IDecompositionQuery[T, *Ts]):
|
89
32
|
@tp.overload
|
90
33
|
def __init__(self, tables: tuple[TableType[T]], columns: tuple[ColumnType]) -> None: ...
|
@@ -93,7 +36,8 @@ class DecompositionQueryBase[T: Table, *Ts](IDecompositionQuery[T, *Ts]):
|
|
93
36
|
@tp.overload
|
94
37
|
def __init__(self, tables: tuple[TableType[T]], columns: tuple[ColumnType], alias_table: str, context: ClauseContextType = ...) -> None: ...
|
95
38
|
|
96
|
-
def __init__(self, tables: tuple[TableType[T]], columns: tuple[ColumnType], alias_table: str = "{table}", *, context: ClauseContextType = ClauseInfoContext()) -> None:
|
39
|
+
def __init__(self, tables: tuple[TableType[T]], columns: tuple[ColumnType], alias_table: str = "{table}", *, context: ClauseContextType = ClauseInfoContext(), **kwargs) -> None:
|
40
|
+
self.kwargs = kwargs
|
97
41
|
self._tables: tuple[TableType[T]] = tables if isinstance(tables, tp.Iterable) else (tables,)
|
98
42
|
|
99
43
|
self._columns: tp.Callable[[T], tuple] = columns
|
@@ -145,7 +89,7 @@ class DecompositionQueryBase[T: Table, *Ts](IDecompositionQuery[T, *Ts]):
|
|
145
89
|
}
|
146
90
|
classConverter = next((converter for obj, converter in VALUE_TYPE_MAPPED.items() if validation(data, obj)), ConvertFromAnyType)
|
147
91
|
|
148
|
-
return classConverter.convert(data, alias_table=self._alias_table, context=self._context)
|
92
|
+
return classConverter.convert(data, alias_table=self._alias_table, context=self._context, **self.kwargs)
|
149
93
|
|
150
94
|
def __add_clause[TTable: TableType](self, clauses: list[ClauseInfo[TTable]] | ClauseInfo[TTable]) -> None:
|
151
95
|
if not isinstance(clauses, tp.Iterable):
|
{ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/common/abstract_classes/non_query_base.py
RENAMED
@@ -5,11 +5,11 @@ from typing import Any, Optional, Type, override, TYPE_CHECKING
|
|
5
5
|
from ormlambda.common.interfaces.INonQueryCommand import INonQueryCommand
|
6
6
|
|
7
7
|
if TYPE_CHECKING:
|
8
|
-
from ormlambda.repository import
|
8
|
+
from ormlambda.repository import BaseRepository
|
9
9
|
from ormlambda import Table
|
10
10
|
|
11
11
|
|
12
|
-
class NonQueryBase[T: Type[Table], TRepo:
|
12
|
+
class NonQueryBase[T: Type[Table], TRepo: BaseRepository](INonQueryCommand):
|
13
13
|
__slots__: tuple[str, ...] = ("_model", "_repository", "_values", "_query")
|
14
14
|
|
15
15
|
def __init__(self, model: T, repository: TRepo) -> None:
|
@@ -3,13 +3,13 @@ from abc import abstractmethod
|
|
3
3
|
from typing import TYPE_CHECKING
|
4
4
|
|
5
5
|
if TYPE_CHECKING:
|
6
|
-
from ormlambda import Table,
|
6
|
+
from ormlambda import Table, BaseRepository
|
7
7
|
from ormlambda.common.abstract_classes import NonQueryBase
|
8
8
|
|
9
9
|
from .IDelete import IDelete
|
10
10
|
|
11
11
|
|
12
|
-
class DeleteQueryBase[T: Table, TRepo:
|
12
|
+
class DeleteQueryBase[T: Table, TRepo: BaseRepository](NonQueryBase[T, TRepo], IDelete[T]):
|
13
13
|
def __init__(self, model: T, repository: TRepo) -> None:
|
14
14
|
super().__init__(model, repository)
|
15
15
|
|
@@ -0,0 +1 @@
|
|
1
|
+
from .join_context import JoinContext, TupleJoinType # noqa: F401
|
@@ -6,7 +6,6 @@ if TYPE_CHECKING:
|
|
6
6
|
from ormlambda import Table
|
7
7
|
from ormlambda.repository import IRepositoryBase
|
8
8
|
from ormlambda.components.delete import DeleteQueryBase
|
9
|
-
from mysql.connector import MySQLConnection
|
10
9
|
|
11
10
|
|
12
11
|
class DeleteQuery[T: Table](DeleteQueryBase[T, IRepositoryBase]):
|
@@ -0,0 +1,26 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
from typing import Literal, override, TYPE_CHECKING
|
3
|
+
|
4
|
+
if TYPE_CHECKING:
|
5
|
+
from mysql.connector import MySQLConnection
|
6
|
+
|
7
|
+
from ormlambda.repository import BaseRepository
|
8
|
+
|
9
|
+
|
10
|
+
TypeExists = Literal["fail", "replace", "append"]
|
11
|
+
|
12
|
+
|
13
|
+
class DropTable:
|
14
|
+
def __init__(self, repository: BaseRepository[MySQLConnection]) -> None:
|
15
|
+
self._repository: BaseRepository[MySQLConnection] = repository
|
16
|
+
|
17
|
+
@override
|
18
|
+
def execute(self, name: str = None) -> None:
|
19
|
+
query = rf"{self.CLAUSE} {name}"
|
20
|
+
self._repository.execute(query)
|
21
|
+
return None
|
22
|
+
|
23
|
+
@property
|
24
|
+
@override
|
25
|
+
def CLAUSE(self) -> str:
|
26
|
+
return "DROP TABLE"
|
@@ -1,7 +1,6 @@
|
|
1
1
|
import typing as tp
|
2
2
|
from ormlambda import Table
|
3
|
-
from ormlambda.sql.clause_info
|
4
|
-
from ormlambda.sql.clause_info.clause_info_context import ClauseInfoContext
|
3
|
+
from ormlambda.sql.clause_info import AggregateFunctionBase, ClauseInfoContext
|
5
4
|
from ormlambda.sql.types import ColumnType
|
6
5
|
|
7
6
|
|
@@ -21,11 +21,13 @@ class Select[T: Type[Table], *Ts](DecompositionQueryBase[T, *Ts], ISelect):
|
|
21
21
|
*,
|
22
22
|
alias_table: AliasType[ClauseInfo] = "{table}",
|
23
23
|
context: Optional[ClauseInfoContext] = None,
|
24
|
+
**kwargs,
|
24
25
|
) -> None:
|
25
26
|
super().__init__(
|
26
27
|
tables,
|
27
28
|
columns,
|
28
29
|
context=context,
|
30
|
+
**kwargs,
|
29
31
|
)
|
30
32
|
self._alias_table = alias_table
|
31
33
|
# We always need to add the self alias of the Select
|
@@ -1,15 +1,19 @@
|
|
1
|
-
from
|
1
|
+
from __future__ import annotations
|
2
|
+
from typing import override, TYPE_CHECKING
|
2
3
|
|
3
4
|
from ormlambda import Table
|
4
5
|
from ormlambda.components.upsert import UpsertQueryBase
|
5
|
-
from ormlambda.repository import IRepositoryBase
|
6
|
-
|
6
|
+
from ormlambda.repository import IRepositoryBase, BaseRepository
|
7
|
+
|
7
8
|
|
8
9
|
from .insert import InsertQuery
|
9
10
|
|
11
|
+
if TYPE_CHECKING:
|
12
|
+
from mysql.connector import MySQLConnection
|
13
|
+
|
10
14
|
|
11
15
|
class UpsertQuery[T: Table](UpsertQueryBase[T, IRepositoryBase]):
|
12
|
-
def __init__(self, model: T, repository:
|
16
|
+
def __init__(self, model: T, repository: BaseRepository[MySQLConnection]) -> None:
|
13
17
|
super().__init__(model, repository)
|
14
18
|
|
15
19
|
@override
|