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.
Files changed (129) hide show
  1. ormlambda-3.11.1/README.md → ormlambda-3.12.2/PKG-INFO +78 -15
  2. ormlambda-3.11.1/PKG-INFO → ormlambda-3.12.2/README.md +64 -28
  3. {ormlambda-3.11.1 → ormlambda-3.12.2}/pyproject.toml +1 -1
  4. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/__init__.py +1 -1
  5. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/caster/caster.py +1 -1
  6. ormlambda-3.12.2/src/ormlambda/common/abstract_classes/clause_info_converter.py +73 -0
  7. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/common/abstract_classes/decomposition_query.py +12 -68
  8. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/common/abstract_classes/non_query_base.py +2 -2
  9. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/common/interfaces/ICustomAlias.py +1 -1
  10. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/components/delete/abstract_delete.py +2 -2
  11. ormlambda-3.12.2/src/ormlambda/components/join/__init__.py +1 -0
  12. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/databases/my_sql/clauses/delete.py +0 -1
  13. ormlambda-3.12.2/src/ormlambda/databases/my_sql/clauses/drop_table.py +26 -0
  14. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/databases/my_sql/clauses/group_by.py +1 -2
  15. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/databases/my_sql/clauses/select.py +2 -0
  16. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/databases/my_sql/clauses/upsert.py +8 -4
  17. ormlambda-3.12.2/src/ormlambda/databases/my_sql/query_builder.py +158 -0
  18. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/databases/my_sql/statements.py +19 -156
  19. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/engine/__init__.py +1 -1
  20. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/engine/url.py +4 -1
  21. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/sql/clause_info/__init__.py +2 -1
  22. ormlambda-3.12.2/src/ormlambda/sql/clause_info/aggregate_function_base.py +86 -0
  23. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/sql/clause_info/clause_info.py +1 -98
  24. ormlambda-3.12.2/src/ormlambda/sql/clause_info/interface/IClauseInfo.py +37 -0
  25. ormlambda-3.12.2/src/ormlambda/sql/clause_info/interface/__init__.py +2 -0
  26. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/sql/column.py +3 -0
  27. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/statements/base_statement.py +6 -2
  28. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/statements/interfaces/IStatements.py +25 -19
  29. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/utils/module_tree/dynamic_module.py +3 -2
  30. ormlambda-3.11.1/src/ormlambda/databases/my_sql/caster/read.py +0 -39
  31. ormlambda-3.11.1/src/ormlambda/databases/my_sql/caster/write.py +0 -37
  32. ormlambda-3.11.1/src/ormlambda/databases/my_sql/clauses/drop_table.py +0 -23
  33. ormlambda-3.11.1/src/ormlambda/sql/clause_info/interface/__init__.py +0 -1
  34. {ormlambda-3.11.1 → ormlambda-3.12.2}/LICENSE +0 -0
  35. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/caster/__init__.py +0 -0
  36. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/caster/base_caster.py +0 -0
  37. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/caster/interfaces/ICaster.py +0 -0
  38. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/caster/interfaces/__init__.py +0 -0
  39. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/common/__init__.py +0 -0
  40. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/common/abstract_classes/__init__.py +0 -0
  41. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/common/abstract_classes/query_base.py +0 -0
  42. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/common/enums/__init__.py +0 -0
  43. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/common/enums/condition_types.py +0 -0
  44. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/common/enums/join_type.py +0 -0
  45. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/common/errors/__init__.py +0 -0
  46. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/common/global_checker.py +0 -0
  47. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/common/interfaces/IDecompositionQuery.py +0 -0
  48. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/common/interfaces/IJoinSelector.py +0 -0
  49. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/common/interfaces/INonQueryCommand.py +0 -0
  50. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/common/interfaces/IQueryCommand.py +0 -0
  51. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/common/interfaces/__init__.py +0 -0
  52. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/components/__init__.py +0 -0
  53. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/components/delete/IDelete.py +0 -0
  54. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/components/delete/__init__.py +0 -0
  55. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/components/insert/IInsert.py +0 -0
  56. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/components/insert/__init__.py +0 -0
  57. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/components/insert/abstract_insert.py +0 -0
  58. {ormlambda-3.11.1/src/ormlambda/databases/my_sql → ormlambda-3.12.2/src/ormlambda/components/join}/join_context.py +0 -0
  59. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/components/select/ISelect.py +0 -0
  60. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/components/select/__init__.py +0 -0
  61. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/components/update/IUpdate.py +0 -0
  62. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/components/update/__init__.py +0 -0
  63. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/components/update/abstract_update.py +0 -0
  64. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/components/upsert/IUpsert.py +0 -0
  65. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/components/upsert/__init__.py +0 -0
  66. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/components/upsert/abstract_upsert.py +0 -0
  67. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/databases/__init__.py +0 -0
  68. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/databases/my_sql/__init__.py +0 -0
  69. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/databases/my_sql/caster/__init__.py +0 -0
  70. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/databases/my_sql/caster/caster.py +0 -0
  71. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/databases/my_sql/caster/types/__init__.py +0 -0
  72. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/databases/my_sql/caster/types/bytes.py +0 -0
  73. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/databases/my_sql/caster/types/datetime.py +0 -0
  74. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/databases/my_sql/caster/types/float.py +0 -0
  75. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/databases/my_sql/caster/types/int.py +0 -0
  76. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/databases/my_sql/caster/types/iterable.py +0 -0
  77. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/databases/my_sql/caster/types/none.py +0 -0
  78. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/databases/my_sql/caster/types/point.py +0 -0
  79. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/databases/my_sql/caster/types/string.py +0 -0
  80. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/databases/my_sql/clauses/ST_AsText.py +0 -0
  81. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/databases/my_sql/clauses/ST_Contains.py +0 -0
  82. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/databases/my_sql/clauses/__init__.py +0 -0
  83. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/databases/my_sql/clauses/alias.py +0 -0
  84. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/databases/my_sql/clauses/count.py +0 -0
  85. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/databases/my_sql/clauses/create_database.py +0 -0
  86. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/databases/my_sql/clauses/drop_database.py +0 -0
  87. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/databases/my_sql/clauses/having.py +0 -0
  88. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/databases/my_sql/clauses/insert.py +0 -0
  89. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/databases/my_sql/clauses/joins.py +0 -0
  90. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/databases/my_sql/clauses/limit.py +0 -0
  91. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/databases/my_sql/clauses/offset.py +0 -0
  92. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/databases/my_sql/clauses/order.py +0 -0
  93. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/databases/my_sql/clauses/update.py +0 -0
  94. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/databases/my_sql/clauses/where.py +0 -0
  95. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/databases/my_sql/functions/__init__.py +0 -0
  96. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/databases/my_sql/functions/concat.py +0 -0
  97. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/databases/my_sql/functions/max.py +0 -0
  98. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/databases/my_sql/functions/min.py +0 -0
  99. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/databases/my_sql/functions/sum.py +0 -0
  100. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/databases/my_sql/repository/__init__.py +0 -0
  101. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/databases/my_sql/repository/repository.py +0 -0
  102. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/databases/my_sql/types.py +0 -0
  103. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/engine/create.py +0 -0
  104. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/engine/template.py +0 -0
  105. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/engine/utils.py +0 -0
  106. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/model/__init__.py +0 -0
  107. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/model/base_model.py +0 -0
  108. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/repository/__init__.py +0 -0
  109. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/repository/base_repository.py +0 -0
  110. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/repository/interfaces/IDatabaseConnection.py +0 -0
  111. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/repository/interfaces/IRepositoryBase.py +0 -0
  112. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/repository/interfaces/__init__.py +0 -0
  113. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/sql/__init__.py +0 -0
  114. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/sql/clause_info/clause_info_context.py +0 -0
  115. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/sql/clause_info/interface/IAggregate.py +0 -0
  116. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/sql/comparer.py +0 -0
  117. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/sql/dtypes.py +0 -0
  118. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/sql/foreign_key.py +0 -0
  119. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/sql/interfaces/__init__.py +0 -0
  120. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/sql/table/__init__.py +0 -0
  121. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/sql/table/fields.py +0 -0
  122. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/sql/table/table_constructor.py +0 -0
  123. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/sql/types.py +0 -0
  124. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/statements/__init__.py +0 -0
  125. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/statements/interfaces/__init__.py +0 -0
  126. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/statements/types.py +0 -0
  127. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/utils/__init__.py +0 -0
  128. {ormlambda-3.11.1 → ormlambda-3.12.2}/src/ormlambda/utils/module_tree/__init__.py +0 -0
  129. {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
  ![PyPI version](https://img.shields.io/pypi/v/ormlambda.svg)
2
16
  ![downloads](https://img.shields.io/pypi/dm/ormlambda?label=downloads)
3
17
  ![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)
@@ -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 = MySQLRepository(**config_dict)
75
+ db = create_engine('mysql://root:1234@localhost:3306/sakila')
61
76
 
62
- AddressModel = ORM(Address,db)
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
- #### Usage
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
- ddbb = MySQLRepository(**config_dict)
351
- model = ORM(Address, ddbb)
420
+ db = create_engine('mysql://root:1234@localhost:3306/sakila')
421
+
352
422
  select = (
353
- model.order(lambda x: x.City.Country.country, "DESC")
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
  ![PyPI version](https://img.shields.io/pypi/v/ormlambda.svg)
15
2
  ![downloads](https://img.shields.io/pypi/dm/ormlambda?label=downloads)
16
3
  ![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)
@@ -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 = MySQLRepository(**config_dict)
61
+ db = create_engine('mysql://root:1234@localhost:3306/sakila')
74
62
 
75
- AddressModel = ORM(Address,db)
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
- #### Usage
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
- ddbb = MySQLRepository(**config_dict)
364
- model = ORM(Address, ddbb)
406
+ db = create_engine('mysql://root:1234@localhost:3306/sakila')
407
+
365
408
  select = (
366
- model.order(lambda x: x.City.Country.country, "DESC")
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.11.1"
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
@@ -15,7 +15,7 @@ if TYPE_CHECKING:
15
15
  PLACEHOLDER: str = "%s"
16
16
 
17
17
 
18
- class Caster[TRepo]:
18
+ class Caster:
19
19
  def __init__(self, repository: IRepositoryBase):
20
20
  self._repository: IRepositoryBase = repository
21
21
  self._caster = RepositoryTemplateDict().get(repository).caster
@@ -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
@@ -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 AliasType, TableType, ColumnType
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):
@@ -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 IRepositoryBase
8
+ from ormlambda.repository import BaseRepository
9
9
  from ormlambda import Table
10
10
 
11
11
 
12
- class NonQueryBase[T: Type[Table], TRepo: IRepositoryBase](INonQueryCommand):
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:
@@ -1,4 +1,4 @@
1
- from ormlambda.common.interfaces import IDecompositionQuery
1
+ # from ormlambda.common.interfaces import IDecompositionQuery
2
2
 
3
3
 
4
4
  class ICustomAlias[T, *Ts]: ...
@@ -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, IRepositoryBase
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: IRepositoryBase](NonQueryBase[T, TRepo], IDelete[T]):
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.clause_info import AggregateFunctionBase
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 typing import override, Any
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
- from mysql.connector import MySQLConnection
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: Any) -> None:
16
+ def __init__(self, model: T, repository: BaseRepository[MySQLConnection]) -> None:
13
17
  super().__init__(model, repository)
14
18
 
15
19
  @override