ormlambda 3.7.2__tar.gz → 3.11.0__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.7.2 → ormlambda-3.11.0}/PKG-INFO +107 -8
- {ormlambda-3.7.2 → ormlambda-3.11.0}/README.md +106 -5
- {ormlambda-3.7.2 → ormlambda-3.11.0}/pyproject.toml +3 -3
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/__init__.py +2 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/databases/my_sql/caster/caster.py +1 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/databases/my_sql/clauses/__init__.py +1 -0
- ormlambda-3.11.0/src/ormlambda/databases/my_sql/clauses/alias.py +28 -0
- ormlambda-3.11.0/src/ormlambda/databases/my_sql/clauses/group_by.py +31 -0
- ormlambda-3.11.0/src/ormlambda/databases/my_sql/clauses/having.py +16 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/databases/my_sql/clauses/order.py +6 -1
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/databases/my_sql/clauses/where.py +3 -3
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/databases/my_sql/functions/concat.py +8 -6
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/databases/my_sql/join_context.py +3 -2
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/databases/my_sql/repository/repository.py +57 -11
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/databases/my_sql/statements.py +72 -21
- ormlambda-3.11.0/src/ormlambda/engine/__init__.py +2 -0
- ormlambda-3.11.0/src/ormlambda/engine/create.py +35 -0
- ormlambda-3.11.0/src/ormlambda/engine/url.py +744 -0
- ormlambda-3.11.0/src/ormlambda/engine/utils.py +17 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/repository/base_repository.py +0 -1
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/sql/column.py +27 -2
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/sql/foreign_key.py +36 -4
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/statements/interfaces/IStatements.py +37 -25
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/statements/types.py +4 -1
- ormlambda-3.7.2/src/ormlambda/databases/my_sql/clauses/alias.py +0 -34
- ormlambda-3.7.2/src/ormlambda/databases/my_sql/clauses/group_by.py +0 -32
- ormlambda-3.7.2/src/ormlambda/sql/interfaces/__init__.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/LICENSE +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/caster/__init__.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/caster/base_caster.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/caster/caster.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/caster/interfaces/ICaster.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/caster/interfaces/__init__.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/common/__init__.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/common/abstract_classes/__init__.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/common/abstract_classes/decomposition_query.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/common/abstract_classes/non_query_base.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/common/abstract_classes/query_base.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/common/enums/__init__.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/common/enums/condition_types.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/common/enums/join_type.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/common/errors/__init__.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/common/global_checker.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/common/interfaces/ICustomAlias.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/common/interfaces/IDecompositionQuery.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/common/interfaces/IJoinSelector.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/common/interfaces/INonQueryCommand.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/common/interfaces/IQueryCommand.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/common/interfaces/__init__.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/components/__init__.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/components/delete/IDelete.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/components/delete/__init__.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/components/delete/abstract_delete.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/components/insert/IInsert.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/components/insert/__init__.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/components/insert/abstract_insert.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/components/select/ISelect.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/components/select/__init__.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/components/update/IUpdate.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/components/update/__init__.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/components/update/abstract_update.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/components/upsert/IUpsert.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/components/upsert/__init__.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/components/upsert/abstract_upsert.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/databases/__init__.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/databases/my_sql/__init__.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/databases/my_sql/caster/__init__.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/databases/my_sql/caster/read.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/databases/my_sql/caster/types/__init__.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/databases/my_sql/caster/types/bytes.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/databases/my_sql/caster/types/datetime.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/databases/my_sql/caster/types/float.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/databases/my_sql/caster/types/int.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/databases/my_sql/caster/types/iterable.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/databases/my_sql/caster/types/none.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/databases/my_sql/caster/types/point.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/databases/my_sql/caster/types/string.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/databases/my_sql/caster/write.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/databases/my_sql/clauses/ST_AsText.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/databases/my_sql/clauses/ST_Contains.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/databases/my_sql/clauses/count.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/databases/my_sql/clauses/create_database.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/databases/my_sql/clauses/delete.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/databases/my_sql/clauses/drop_database.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/databases/my_sql/clauses/drop_table.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/databases/my_sql/clauses/insert.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/databases/my_sql/clauses/joins.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/databases/my_sql/clauses/limit.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/databases/my_sql/clauses/offset.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/databases/my_sql/clauses/select.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/databases/my_sql/clauses/update.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/databases/my_sql/clauses/upsert.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/databases/my_sql/functions/__init__.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/databases/my_sql/functions/max.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/databases/my_sql/functions/min.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/databases/my_sql/functions/sum.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/databases/my_sql/repository/__init__.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/databases/my_sql/types.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/engine/template.py +0 -0
- {ormlambda-3.7.2/src/ormlambda/engine → ormlambda-3.11.0/src/ormlambda/model}/__init__.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/model/base_model.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/repository/__init__.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/repository/interfaces/IDatabaseConnection.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/repository/interfaces/IRepositoryBase.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/repository/interfaces/__init__.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/sql/__init__.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/sql/clause_info/__init__.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/sql/clause_info/clause_info.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/sql/clause_info/clause_info_context.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/sql/clause_info/interface/IAggregate.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/sql/clause_info/interface/__init__.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/sql/comparer.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/sql/dtypes.py +0 -0
- {ormlambda-3.7.2/src/ormlambda/model → ormlambda-3.11.0/src/ormlambda/sql/interfaces}/__init__.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/sql/table/__init__.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/sql/table/fields.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/sql/table/table_constructor.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/sql/types.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/statements/__init__.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/statements/base_statement.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/statements/interfaces/__init__.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/utils/__init__.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/utils/module_tree/__init__.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/utils/module_tree/dfs_traversal.py +0 -0
- {ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/utils/module_tree/dynamic_module.py +0 -0
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: ormlambda
|
|
3
|
-
Version: 3.
|
|
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
|

|
|
18
16
|

|
|
19
17
|
|
|
20
|
-
#
|
|
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
|
|
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 =
|
|
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
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|

|
|
3
3
|

|
|
4
4
|
|
|
5
|
-
#
|
|
5
|
+
# ormlambda Documentation
|
|
6
6
|
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.
|
|
7
7
|
|
|
8
8
|
# Creating your first lambda query
|
|
@@ -23,19 +23,34 @@ database = MySQLRepository(user=USERNAME, password=PASSWORD, database="sakila",
|
|
|
23
23
|
## Select all columns
|
|
24
24
|
```python
|
|
25
25
|
from ormlambda import ORM
|
|
26
|
-
from ormlambda
|
|
26
|
+
from ormlambda import create_engine
|
|
27
27
|
|
|
28
28
|
from models.address import Address
|
|
29
|
-
from config import config_dict
|
|
29
|
+
from test.config import config_dict
|
|
30
30
|
|
|
31
|
-
db =
|
|
31
|
+
db = create_engine('mysql://root:1234@localhost:3306/sakila')
|
|
32
32
|
|
|
33
|
-
AddressModel = ORM(Address,db)
|
|
33
|
+
AddressModel = ORM(Address, db)
|
|
34
34
|
|
|
35
35
|
result = AddressModel.select()
|
|
36
36
|
```
|
|
37
37
|
The `result` var will be of type `tuple[Address, ...]`
|
|
38
38
|
|
|
39
|
+
## Improving Typing
|
|
40
|
+
For those cases where you need to pass the database configuration from a `dict`, you can use `MySQLArgs` TypedDict object to improve type annotations.
|
|
41
|
+
|
|
42
|
+
```python
|
|
43
|
+
from ormlambda.databases.my_sql.types import MySQLArgs
|
|
44
|
+
|
|
45
|
+
config_dict: MySQLArgs = {
|
|
46
|
+
"user": DB_USERNAME,
|
|
47
|
+
"password": DB_PASSWORD,
|
|
48
|
+
"host": DB_HOST,
|
|
49
|
+
"database": DB_DATABASE,
|
|
50
|
+
}
|
|
51
|
+
db = MySQLRepository(**config_dict)
|
|
52
|
+
```
|
|
53
|
+
|
|
39
54
|
## Select multiples tables
|
|
40
55
|
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."
|
|
41
56
|
|
|
@@ -281,6 +296,92 @@ res = AddressModel.sum(Address.address_id, execute=True)
|
|
|
281
296
|
res = AddressModel.count(Address.address_id, execute=True)
|
|
282
297
|
```
|
|
283
298
|
|
|
299
|
+
## 1. Concat
|
|
300
|
+
|
|
301
|
+
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
|
+
|
|
303
|
+
#### Usage
|
|
304
|
+
|
|
305
|
+
```python
|
|
306
|
+
response = ORM(Address, db).where(Address.City.Country.country.regex(r"^Spain")).first(
|
|
307
|
+
(
|
|
308
|
+
Address.address,
|
|
309
|
+
Address.City.city,
|
|
310
|
+
self.tmodel.concat(
|
|
311
|
+
(
|
|
312
|
+
"Address: ",
|
|
313
|
+
Address.address,
|
|
314
|
+
" - city: ",
|
|
315
|
+
Address.City.city,
|
|
316
|
+
" - country: ",
|
|
317
|
+
Address.City.Country.country,
|
|
318
|
+
)
|
|
319
|
+
),
|
|
320
|
+
),
|
|
321
|
+
flavour=dict,
|
|
322
|
+
)
|
|
323
|
+
|
|
324
|
+
{
|
|
325
|
+
"address_address": "939 Probolinggo Loop",
|
|
326
|
+
"city_city": "A Coruña (La Coruña)",
|
|
327
|
+
"CONCAT": "Address: 939 Probolinggo Loop - city: A Coruña (La Coruña) - country: Spain",
|
|
328
|
+
}
|
|
329
|
+
```
|
|
330
|
+
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.
|
|
331
|
+
|
|
332
|
+
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
|
+
|
|
334
|
+
## Using BaseModel for Custom Responses (Pydantic)
|
|
335
|
+
|
|
336
|
+
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.
|
|
337
|
+
|
|
338
|
+
### Example: Creating a Custom Response Model
|
|
339
|
+
|
|
340
|
+
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.
|
|
341
|
+
|
|
342
|
+
```python
|
|
343
|
+
class AddressCombine(BaseModel):
|
|
344
|
+
address: str
|
|
345
|
+
city: str
|
|
346
|
+
country: str
|
|
347
|
+
|
|
348
|
+
model_config: ConfigDict = {"extra": "forbid"}
|
|
349
|
+
|
|
350
|
+
ddbb = MySQLRepository(**config_dict)
|
|
351
|
+
model = ORM(Address, ddbb)
|
|
352
|
+
select = (
|
|
353
|
+
model.order(lambda x: x.City.Country.country, "DESC")
|
|
354
|
+
.limit(10)
|
|
355
|
+
.where(Address.City.Country.country == "Spain")
|
|
356
|
+
.first(
|
|
357
|
+
lambda x: (
|
|
358
|
+
x.address,
|
|
359
|
+
x.City.city,
|
|
360
|
+
x.City.Country.country,
|
|
361
|
+
),
|
|
362
|
+
flavour=AddressCombine,
|
|
363
|
+
)
|
|
364
|
+
)
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
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.
|
|
368
|
+
|
|
369
|
+
```python
|
|
370
|
+
|
|
371
|
+
|
|
372
|
+
print(select.address)
|
|
373
|
+
print(select.city)
|
|
374
|
+
print(select.country)
|
|
375
|
+
```
|
|
376
|
+
|
|
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
|
+
|
|
284
385
|
## Combine aggregation method
|
|
285
386
|
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.
|
|
286
387
|
```python
|
|
@@ -3,7 +3,7 @@ line-length = 320
|
|
|
3
3
|
|
|
4
4
|
[tool.poetry]
|
|
5
5
|
name = "ormlambda"
|
|
6
|
-
version = "3.
|
|
6
|
+
version = "3.11.0"
|
|
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"
|
|
@@ -11,11 +11,11 @@ readme = "README.md"
|
|
|
11
11
|
[tool.poetry.dependencies]
|
|
12
12
|
python = "^3.12"
|
|
13
13
|
mysql-connector-python= "^9.0.0"
|
|
14
|
-
fluent-validation = "4.3.1"
|
|
15
14
|
shapely = "^2.0.6"
|
|
16
|
-
python-dotenv = "^1.0.1"
|
|
17
15
|
|
|
18
16
|
[tool.poetry.group.test.dependencies]
|
|
17
|
+
fluent-validation = "4.3.1"
|
|
18
|
+
python-dotenv = "^1.0.1"
|
|
19
19
|
pandas = "^2.2.2"
|
|
20
20
|
ruff = "^0.4.5"
|
|
21
21
|
parameterized = "^0.9.0"
|
|
@@ -20,3 +20,5 @@ from .model.base_model import (
|
|
|
20
20
|
BaseModel as BaseModel,
|
|
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
|
+
|
|
24
|
+
from .engine import create_engine, URL # noqa: F401
|
|
@@ -12,6 +12,7 @@ from .order import Order as Order
|
|
|
12
12
|
from .update import UpdateQuery as UpdateQuery
|
|
13
13
|
from .upsert import UpsertQuery as UpsertQuery
|
|
14
14
|
from .where import Where as Where
|
|
15
|
+
from .having import Having as Having
|
|
15
16
|
from .count import Count as Count
|
|
16
17
|
from .group_by import GroupBy as GroupBy
|
|
17
18
|
from .alias import Alias as Alias
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
import typing as tp
|
|
3
|
+
|
|
4
|
+
from ormlambda import Table
|
|
5
|
+
from ormlambda.sql.clause_info import ClauseInfo
|
|
6
|
+
from ormlambda.sql.clause_info.clause_info_context import ClauseContextType
|
|
7
|
+
from ormlambda.sql.types import TableType
|
|
8
|
+
|
|
9
|
+
if tp.TYPE_CHECKING:
|
|
10
|
+
from ormlambda.sql.types import ColumnType
|
|
11
|
+
from ormlambda import Table
|
|
12
|
+
from ormlambda.sql.types import AliasType
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Alias[T: Table](ClauseInfo[T]):
|
|
16
|
+
def __init__[TProp](
|
|
17
|
+
self,
|
|
18
|
+
table: TableType[T],
|
|
19
|
+
column: tp.Optional[ColumnType[TProp]] = None,
|
|
20
|
+
alias_table: tp.Optional[AliasType[ClauseInfo[T]]] = None,
|
|
21
|
+
alias_clause: tp.Optional[AliasType[ClauseInfo[T]]] = None,
|
|
22
|
+
context: ClauseContextType = None,
|
|
23
|
+
keep_asterisk: bool = False,
|
|
24
|
+
preserve_context: bool = False,
|
|
25
|
+
):
|
|
26
|
+
if not alias_clause:
|
|
27
|
+
raise TypeError
|
|
28
|
+
super().__init__(table, column, alias_table, alias_clause, context, keep_asterisk, preserve_context)
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import typing as tp
|
|
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
|
|
5
|
+
from ormlambda.sql.types import ColumnType
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class GroupBy[T: tp.Type[Table], *Ts, TProp](AggregateFunctionBase):
|
|
9
|
+
@classmethod
|
|
10
|
+
def FUNCTION_NAME(self) -> str:
|
|
11
|
+
return "GROUP BY"
|
|
12
|
+
|
|
13
|
+
def __init__(
|
|
14
|
+
self,
|
|
15
|
+
column: ColumnType,
|
|
16
|
+
context: ClauseInfoContext,
|
|
17
|
+
**kwargs,
|
|
18
|
+
):
|
|
19
|
+
super().__init__(
|
|
20
|
+
table=column.table,
|
|
21
|
+
column=column,
|
|
22
|
+
alias_table=None,
|
|
23
|
+
alias_clause=None,
|
|
24
|
+
context=context,
|
|
25
|
+
**kwargs,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
@property
|
|
29
|
+
def query(self) -> str:
|
|
30
|
+
column = self._create_query()
|
|
31
|
+
return f"{self.FUNCTION_NAME()} {column}"
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from .where import Where
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Having(Where):
|
|
7
|
+
"""
|
|
8
|
+
The purpose of this class is to create 'WHERE' condition queries properly.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
def __init__(self, *comparer, restrictive=True, context=None):
|
|
12
|
+
super().__init__(*comparer, restrictive=restrictive, context=context)
|
|
13
|
+
|
|
14
|
+
@staticmethod
|
|
15
|
+
def FUNCTION_NAME() -> str:
|
|
16
|
+
return "HAVING"
|
|
@@ -48,13 +48,18 @@ class Order(AggregateFunctionBase):
|
|
|
48
48
|
columns = self.unresolved_column
|
|
49
49
|
|
|
50
50
|
# if this attr is not iterable means that we only pass one column without wrapped in a list or tuple
|
|
51
|
+
if isinstance(columns, str):
|
|
52
|
+
string_columns = f"{columns} {str(self._order_type[0])}"
|
|
53
|
+
return f"{self.FUNCTION_NAME()} {string_columns}"
|
|
54
|
+
|
|
51
55
|
if not isinstance(columns, tp.Iterable):
|
|
52
56
|
columns = (columns,)
|
|
57
|
+
|
|
53
58
|
assert len(columns) == len(self._order_type)
|
|
54
59
|
|
|
55
60
|
context = ClauseInfoContext(table_context=self._context._table_context, clause_context=None) if self._context else None
|
|
56
61
|
for index, clause in enumerate(self._convert_into_clauseInfo(columns, context)):
|
|
57
62
|
clause.alias_clause = None
|
|
58
|
-
string_columns.append(f"{clause.query} {self._order_type[index]
|
|
63
|
+
string_columns.append(f"{clause.query} {str(self._order_type[index])}")
|
|
59
64
|
|
|
60
65
|
return f"{self.FUNCTION_NAME()} {', '.join(string_columns)}"
|
|
@@ -32,8 +32,8 @@ class Where(AggregateFunctionBase):
|
|
|
32
32
|
def alias_clause(self) -> None:
|
|
33
33
|
return None
|
|
34
34
|
|
|
35
|
-
@
|
|
36
|
-
def join_condition(wheres: tp.Iterable[Where], restrictive: bool, context: ClauseInfoContext) -> str:
|
|
35
|
+
@classmethod
|
|
36
|
+
def join_condition(cls, wheres: tp.Iterable[Where], restrictive: bool, context: ClauseInfoContext) -> str:
|
|
37
37
|
if not isinstance(wheres, tp.Iterable):
|
|
38
38
|
wheres = (wheres,)
|
|
39
39
|
|
|
@@ -42,4 +42,4 @@ class Where(AggregateFunctionBase):
|
|
|
42
42
|
for c in where._comparer:
|
|
43
43
|
c.set_context(context)
|
|
44
44
|
comparers.append(c)
|
|
45
|
-
return
|
|
45
|
+
return cls(*comparers, restrictive=restrictive, context=context).query
|
|
@@ -1,20 +1,22 @@
|
|
|
1
|
+
import typing as tp
|
|
2
|
+
|
|
1
3
|
from ormlambda.sql.clause_info import AggregateFunctionBase
|
|
2
4
|
from ormlambda.sql.clause_info.clause_info_context import ClauseInfoContext, ClauseContextType
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
import typing as tp
|
|
6
5
|
from ormlambda.sql.types import ColumnType, AliasType
|
|
7
6
|
from ormlambda.sql.clause_info import ClauseInfo
|
|
8
7
|
|
|
9
8
|
|
|
10
|
-
|
|
9
|
+
type ConcatResponse[TProp] = tuple[str | ColumnType[TProp]]
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class Concat[T](AggregateFunctionBase):
|
|
11
13
|
@staticmethod
|
|
12
14
|
def FUNCTION_NAME() -> str:
|
|
13
15
|
return "CONCAT"
|
|
14
16
|
|
|
15
17
|
def __init__[TProp](
|
|
16
18
|
self,
|
|
17
|
-
values:
|
|
19
|
+
values: ConcatResponse[TProp],
|
|
18
20
|
alias_clause: AliasType[ColumnType[TProp]] = "concat",
|
|
19
21
|
context: ClauseContextType = None,
|
|
20
22
|
) -> None:
|
|
@@ -33,6 +35,6 @@ class Concat[*Ts](AggregateFunctionBase):
|
|
|
33
35
|
context = ClauseInfoContext(table_context=self._context._table_context, clause_context=None) if self._context else None
|
|
34
36
|
|
|
35
37
|
for clause in self._convert_into_clauseInfo(self.unresolved_column, context=context):
|
|
36
|
-
clause.alias_clause =
|
|
38
|
+
clause.alias_clause = self.alias_clause
|
|
37
39
|
columns.append(clause)
|
|
38
40
|
return self._concat_alias_and_column(f"{self.FUNCTION_NAME()}({ClauseInfo.join_clauses(columns)})", self._alias_aggregate)
|
|
@@ -27,12 +27,12 @@ class JoinContext[TParent: Table, TRepo]:
|
|
|
27
27
|
for comparer, by in self._joins:
|
|
28
28
|
fk_clause, alias = self.get_fk_clause(comparer)
|
|
29
29
|
|
|
30
|
-
foreign_key: ForeignKey = ForeignKey(comparer=comparer, clause_name=alias)
|
|
30
|
+
foreign_key: ForeignKey = ForeignKey(comparer=comparer, clause_name=alias, keep_alive=True)
|
|
31
31
|
fk_clause.alias_table = foreign_key.alias
|
|
32
32
|
self._context.add_clause_to_context(fk_clause)
|
|
33
33
|
setattr(self._parent, alias, foreign_key)
|
|
34
34
|
|
|
35
|
-
# TODOH []: We need to preserve the 'foreign_key' variable while inside the 'with' clause.
|
|
35
|
+
# TODOH [x]: We need to preserve the 'foreign_key' variable while inside the 'with' clause.
|
|
36
36
|
# Keep in mind that 'ForeignKey.stored_calls' is cleared every time we call methods like
|
|
37
37
|
# .select(), .select_one(), .insert(), .update(), or .count(). This means we only retain
|
|
38
38
|
# the context from the first call of any of these methods.
|
|
@@ -49,6 +49,7 @@ class JoinContext[TParent: Table, TRepo]:
|
|
|
49
49
|
fk: ForeignKey = getattr(self._parent, attribute)
|
|
50
50
|
delattr(self._parent, attribute)
|
|
51
51
|
del self._context._table_context[fk.tright]
|
|
52
|
+
ForeignKey.stored_calls.remove(fk)
|
|
52
53
|
return None
|
|
53
54
|
|
|
54
55
|
def __getattr__(self, name: str) -> TParent:
|
{ormlambda-3.7.2 → ormlambda-3.11.0}/src/ormlambda/databases/my_sql/repository/repository.py
RENAMED
|
@@ -2,6 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
import contextlib
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
from typing import Any, Generator, Iterable, Optional, Type, override, TYPE_CHECKING, Unpack
|
|
5
|
+
import uuid
|
|
5
6
|
import shapely as shp
|
|
6
7
|
|
|
7
8
|
# from mysql.connector.pooling import MySQLConnectionPool
|
|
@@ -16,6 +17,7 @@ from ormlambda.caster import Caster
|
|
|
16
17
|
from ..clauses import CreateDatabase, TypeExists
|
|
17
18
|
from ..clauses import DropDatabase
|
|
18
19
|
from ..clauses import DropTable
|
|
20
|
+
from ..clauses import Alias
|
|
19
21
|
|
|
20
22
|
|
|
21
23
|
if TYPE_CHECKING:
|
|
@@ -68,12 +70,15 @@ class Response[TFlavour, *Ts]:
|
|
|
68
70
|
|
|
69
71
|
def _cast_to_flavour(self, data: list[tuple[*Ts]], **kwargs) -> list[dict[str, tuple[*Ts]]] | list[tuple[*Ts]] | list[TFlavour]:
|
|
70
72
|
def _dict(**kwargs) -> list[dict[str, tuple[*Ts]]]:
|
|
73
|
+
nonlocal data
|
|
71
74
|
return [dict(zip(self._columns, x)) for x in data]
|
|
72
75
|
|
|
73
76
|
def _tuple(**kwargs) -> list[tuple[*Ts]]:
|
|
77
|
+
nonlocal data
|
|
74
78
|
return data
|
|
75
79
|
|
|
76
80
|
def _set(**kwargs) -> list[set]:
|
|
81
|
+
nonlocal data
|
|
77
82
|
for d in data:
|
|
78
83
|
n = len(d)
|
|
79
84
|
for i in range(n):
|
|
@@ -84,12 +89,19 @@ class Response[TFlavour, *Ts]:
|
|
|
84
89
|
return [set(x) for x in data]
|
|
85
90
|
|
|
86
91
|
def _list(**kwargs) -> list[list]:
|
|
92
|
+
nonlocal data
|
|
87
93
|
return [list(x) for x in data]
|
|
88
94
|
|
|
89
95
|
def _default(**kwargs) -> list[TFlavour]:
|
|
90
|
-
|
|
96
|
+
nonlocal data
|
|
97
|
+
replacer_dicc: dict[str, str] = {}
|
|
91
98
|
|
|
92
|
-
|
|
99
|
+
for col in self._select.all_clauses:
|
|
100
|
+
if hasattr(col, "_alias_aggregate") or col.alias_clause is None or isinstance(col, Alias):
|
|
101
|
+
continue
|
|
102
|
+
replacer_dicc[col.alias_clause] = col.column
|
|
103
|
+
|
|
104
|
+
cleaned_column_names = [replacer_dicc.get(col, col) for col in self._columns]
|
|
93
105
|
|
|
94
106
|
result = []
|
|
95
107
|
for attr in data:
|
|
@@ -157,7 +169,34 @@ class MySQLRepository(BaseRepository[MySQLConnectionPool]):
|
|
|
157
169
|
#
|
|
158
170
|
|
|
159
171
|
def __init__(self, **kwargs: Unpack[MySQLArgs]):
|
|
160
|
-
|
|
172
|
+
timeout = self.__add_connection_timeout(kwargs)
|
|
173
|
+
name = self.__add_pool_name(kwargs)
|
|
174
|
+
size = self.__add_pool_size(kwargs)
|
|
175
|
+
attr = kwargs.copy()
|
|
176
|
+
attr["connection_timeout"] = timeout
|
|
177
|
+
attr["pool_name"] = name
|
|
178
|
+
attr["pool_size"] = size
|
|
179
|
+
|
|
180
|
+
super().__init__(MySQLConnectionPool, **attr)
|
|
181
|
+
|
|
182
|
+
@staticmethod
|
|
183
|
+
def __add_connection_timeout(kwargs: MySQLArgs) -> int:
|
|
184
|
+
if "connection_timeout" not in kwargs.keys():
|
|
185
|
+
return 60
|
|
186
|
+
return int(kwargs.pop("connection_timeout"))
|
|
187
|
+
|
|
188
|
+
@staticmethod
|
|
189
|
+
def __add_pool_name(kwargs: MySQLArgs) -> str:
|
|
190
|
+
if "pool_name" not in kwargs.keys():
|
|
191
|
+
return str(uuid.uuid4())
|
|
192
|
+
|
|
193
|
+
return kwargs.pop("pool_name")
|
|
194
|
+
|
|
195
|
+
@staticmethod
|
|
196
|
+
def __add_pool_size(kwargs: MySQLArgs) -> int:
|
|
197
|
+
if "pool_size" not in kwargs.keys():
|
|
198
|
+
return 5
|
|
199
|
+
return int(kwargs.pop("pool_size"))
|
|
161
200
|
|
|
162
201
|
@contextlib.contextmanager
|
|
163
202
|
def get_connection(self) -> Generator[MySQLConnection, None, None]:
|
|
@@ -254,7 +293,6 @@ class MySQLRepository(BaseRepository[MySQLConnectionPool]):
|
|
|
254
293
|
|
|
255
294
|
@override
|
|
256
295
|
def database_exists(self, name: str) -> bool:
|
|
257
|
-
query = "SHOW DATABASES LIKE %s;"
|
|
258
296
|
temp_config = self._pool._cnx_config
|
|
259
297
|
|
|
260
298
|
config_without_db = temp_config.copy()
|
|
@@ -262,10 +300,11 @@ class MySQLRepository(BaseRepository[MySQLConnectionPool]):
|
|
|
262
300
|
if "database" in config_without_db:
|
|
263
301
|
config_without_db.pop("database")
|
|
264
302
|
self._pool.set_config(**config_without_db)
|
|
303
|
+
|
|
265
304
|
with self.get_connection() as cnx:
|
|
266
305
|
with cnx.cursor(buffered=True) as cursor:
|
|
267
|
-
cursor.execute(
|
|
268
|
-
|
|
306
|
+
cursor.execute("SHOW DATABASES LIKE %s;", (name,))
|
|
307
|
+
res = cursor.fetchmany(1)
|
|
269
308
|
|
|
270
309
|
self._pool.set_config(**temp_config)
|
|
271
310
|
return len(res) > 0
|
|
@@ -276,12 +315,11 @@ class MySQLRepository(BaseRepository[MySQLConnectionPool]):
|
|
|
276
315
|
|
|
277
316
|
@override
|
|
278
317
|
def table_exists(self, name: str) -> bool:
|
|
279
|
-
query = "SHOW TABLES LIKE %s;"
|
|
280
318
|
with self.get_connection() as cnx:
|
|
281
319
|
if not cnx.database:
|
|
282
320
|
raise Exception("No database selected")
|
|
283
321
|
with cnx.cursor(buffered=True) as cursor:
|
|
284
|
-
cursor.execute(
|
|
322
|
+
cursor.execute("SHOW TABLES LIKE %s;", (name,))
|
|
285
323
|
res = cursor.fetchmany(1)
|
|
286
324
|
return len(res) > 0
|
|
287
325
|
|
|
@@ -297,9 +335,17 @@ class MySQLRepository(BaseRepository[MySQLConnectionPool]):
|
|
|
297
335
|
|
|
298
336
|
@property
|
|
299
337
|
def database(self) -> Optional[str]:
|
|
300
|
-
return self.
|
|
338
|
+
return self._pool._cnx_config.get("database", None)
|
|
301
339
|
|
|
302
340
|
@database.setter
|
|
303
341
|
def database(self, value: str) -> None:
|
|
304
|
-
|
|
305
|
-
|
|
342
|
+
"""Change the current database using USE statement"""
|
|
343
|
+
|
|
344
|
+
if not self.database_exists(value):
|
|
345
|
+
raise ValueError(f"You cannot set the non-existent '{value}' database.")
|
|
346
|
+
|
|
347
|
+
old_config: MySQLArgs = self._pool._cnx_config.copy()
|
|
348
|
+
old_config["database"] = value
|
|
349
|
+
|
|
350
|
+
self._pool._remove_connections()
|
|
351
|
+
self._pool = type(self)(**old_config)._pool
|