ormlambda 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. ormlambda/__init__.py +4 -0
  2. ormlambda/common/__init__.py +2 -0
  3. ormlambda/common/abstract_classes/__init__.py +3 -0
  4. ormlambda/common/abstract_classes/abstract_model.py +302 -0
  5. ormlambda/common/abstract_classes/non_query_base.py +33 -0
  6. ormlambda/common/abstract_classes/query_base.py +10 -0
  7. ormlambda/common/enums/__init__.py +2 -0
  8. ormlambda/common/enums/condition_types.py +16 -0
  9. ormlambda/common/enums/join_type.py +10 -0
  10. ormlambda/common/interfaces/INonQueryCommand.py +9 -0
  11. ormlambda/common/interfaces/IQueryCommand.py +11 -0
  12. ormlambda/common/interfaces/IRepositoryBase.py +67 -0
  13. ormlambda/common/interfaces/IStatements.py +227 -0
  14. ormlambda/common/interfaces/__init__.py +4 -0
  15. ormlambda/components/__init__.py +0 -0
  16. ormlambda/components/delete/IDelete.py +6 -0
  17. ormlambda/components/delete/__init__.py +2 -0
  18. ormlambda/components/delete/abstract_delete.py +14 -0
  19. ormlambda/components/insert/IInsert.py +6 -0
  20. ormlambda/components/insert/__init__.py +2 -0
  21. ormlambda/components/insert/abstract_insert.py +21 -0
  22. ormlambda/components/select/ISelect.py +14 -0
  23. ormlambda/components/select/__init__.py +2 -0
  24. ormlambda/components/select/table_column.py +39 -0
  25. ormlambda/components/update/IUpdate.py +7 -0
  26. ormlambda/components/update/__init__.py +2 -0
  27. ormlambda/components/update/abstract_update.py +25 -0
  28. ormlambda/components/upsert/IUpsert.py +6 -0
  29. ormlambda/components/upsert/__init__.py +2 -0
  30. ormlambda/components/upsert/abstract_upsert.py +21 -0
  31. ormlambda/components/where/__init__.py +1 -0
  32. ormlambda/components/where/abstract_where.py +11 -0
  33. ormlambda/databases/__init__.py +0 -0
  34. ormlambda/databases/my_sql/__init__.py +2 -0
  35. ormlambda/databases/my_sql/clauses/__init__.py +13 -0
  36. ormlambda/databases/my_sql/clauses/create_database.py +29 -0
  37. ormlambda/databases/my_sql/clauses/delete.py +54 -0
  38. ormlambda/databases/my_sql/clauses/drop_database.py +19 -0
  39. ormlambda/databases/my_sql/clauses/drop_table.py +23 -0
  40. ormlambda/databases/my_sql/clauses/insert.py +70 -0
  41. ormlambda/databases/my_sql/clauses/joins.py +103 -0
  42. ormlambda/databases/my_sql/clauses/limit.py +17 -0
  43. ormlambda/databases/my_sql/clauses/offset.py +17 -0
  44. ormlambda/databases/my_sql/clauses/order.py +29 -0
  45. ormlambda/databases/my_sql/clauses/select.py +172 -0
  46. ormlambda/databases/my_sql/clauses/update.py +52 -0
  47. ormlambda/databases/my_sql/clauses/upsert.py +68 -0
  48. ormlambda/databases/my_sql/clauses/where_condition.py +219 -0
  49. ormlambda/databases/my_sql/repository.py +192 -0
  50. ormlambda/databases/my_sql/statements.py +86 -0
  51. ormlambda/model_base.py +36 -0
  52. ormlambda/utils/__init__.py +3 -0
  53. ormlambda/utils/column.py +65 -0
  54. ormlambda/utils/dtypes.py +104 -0
  55. ormlambda/utils/foreign_key.py +36 -0
  56. ormlambda/utils/lambda_disassembler/__init__.py +4 -0
  57. ormlambda/utils/lambda_disassembler/dis_types.py +136 -0
  58. ormlambda/utils/lambda_disassembler/disassembler.py +69 -0
  59. ormlambda/utils/lambda_disassembler/dtypes.py +103 -0
  60. ormlambda/utils/lambda_disassembler/name_of.py +41 -0
  61. ormlambda/utils/lambda_disassembler/nested_element.py +44 -0
  62. ormlambda/utils/lambda_disassembler/tree_instruction.py +145 -0
  63. ormlambda/utils/module_tree/__init__.py +0 -0
  64. ormlambda/utils/module_tree/dfs_traversal.py +60 -0
  65. ormlambda/utils/module_tree/dynamic_module.py +237 -0
  66. ormlambda/utils/table_constructor.py +308 -0
  67. ormlambda-0.1.0.dist-info/LICENSE +21 -0
  68. ormlambda-0.1.0.dist-info/METADATA +268 -0
  69. ormlambda-0.1.0.dist-info/RECORD +70 -0
  70. ormlambda-0.1.0.dist-info/WHEEL +4 -0
@@ -0,0 +1,103 @@
1
+ from typing import override, Callable, overload, Optional, TypeVar
2
+
3
+ # from ..table import Table
4
+
5
+ from ....common.interfaces.IQueryCommand import IQuery
6
+ from ....utils.lambda_disassembler import Disassembler
7
+ from ....common.enums import JoinType
8
+
9
+ # TODOL: Try to import Table module without circular import Error
10
+ Table = TypeVar("Table")
11
+
12
+
13
+ class JoinSelector[TLeft, TRight](IQuery):
14
+ __slots__: tuple = (
15
+ "_orig_table",
16
+ "_table_right",
17
+ "_by",
18
+ "_left_col",
19
+ "_right_col",
20
+ "_compareop",
21
+ )
22
+
23
+ @overload
24
+ def __init__(
25
+ self,
26
+ table_left: TLeft,
27
+ table_right: TRight,
28
+ col_left: str,
29
+ col_right: str,
30
+ by: JoinType,
31
+ ) -> None: ...
32
+
33
+ @overload
34
+ def __init__(
35
+ self,
36
+ table_left: TLeft,
37
+ table_right: TRight,
38
+ by: JoinType,
39
+ where: Callable[[TLeft, TRight], bool],
40
+ ) -> None: ...
41
+
42
+ def __init__(
43
+ self,
44
+ table_left: Table,
45
+ table_right: Table,
46
+ by: JoinType,
47
+ col_left: Optional[str] = None,
48
+ col_right: Optional[str] = None,
49
+ where: Optional[Callable[[TLeft, TRight], bool]] = None,
50
+ ) -> None:
51
+ self._orig_table: Table = table_left
52
+ self._table_right: Table = table_right
53
+ self._by: JoinType = by
54
+
55
+ if all(x is None for x in (col_left, col_right, where)):
56
+ raise ValueError("You must specify at least 'where' clausule or ('_left_col',_right_col')")
57
+
58
+ if where is None:
59
+ self._left_col: str = col_left
60
+ self._right_col: str = col_right
61
+ self._compareop: str = "="
62
+ else:
63
+ _dis: Disassembler[TLeft, TRight] = Disassembler[TLeft, TRight](where)
64
+ self._left_col: str = _dis.cond_1.name
65
+ self._right_col: str = _dis.cond_2.name
66
+ self._compareop: str = _dis.compare_op
67
+
68
+ def __eq__(self, __value: "JoinSelector") -> bool:
69
+ return isinstance(__value, JoinSelector) and self.__hash__() == __value.__hash__()
70
+
71
+ def __hash__(self) -> int:
72
+ return hash(
73
+ (
74
+ self._orig_table,
75
+ self._table_right,
76
+ self._by,
77
+ self._left_col,
78
+ self._right_col,
79
+ self._compareop,
80
+ )
81
+ )
82
+
83
+ @classmethod
84
+ def join_selectors(cls, *args: "JoinSelector") -> str:
85
+ return "\n".join([x.query for x in args])
86
+
87
+ @property
88
+ @override
89
+ def query(self) -> str:
90
+ # {inner join} table_name on
91
+ # table_name.first col = table_name.second_col
92
+
93
+ left_col = f"{self._orig_table.__table_name__}.{self._left_col}"
94
+ right_col = f"{self._table_right.__table_name__}.{self._right_col}"
95
+ list_ = [
96
+ self._by.value, # inner join
97
+ self._table_right.__table_name__, # table_name
98
+ "ON",
99
+ left_col, # first_col
100
+ self._compareop, # =
101
+ right_col, # second_col
102
+ ]
103
+ return " ".join(list_)
@@ -0,0 +1,17 @@
1
+ from typing import override
2
+
3
+ from ....common.interfaces.IQueryCommand import IQuery
4
+
5
+
6
+ class LimitQuery(IQuery):
7
+ LIMIT = "LIMIT"
8
+
9
+ def __init__(self, number: int) -> None:
10
+ if not isinstance(number, int):
11
+ raise ValueError
12
+ self._number: int = number
13
+
14
+ @override
15
+ @property
16
+ def query(self) -> str:
17
+ return f"{self.LIMIT} {self._number}"
@@ -0,0 +1,17 @@
1
+ from typing import override
2
+
3
+ from ....common.interfaces.IQueryCommand import IQuery
4
+
5
+
6
+ class OffsetQuery(IQuery):
7
+ OFFSET = "OFFSET"
8
+
9
+ def __init__(self, number: int) -> None:
10
+ if not isinstance(number, int):
11
+ raise ValueError
12
+ self._number: int = number
13
+
14
+ @override
15
+ @property
16
+ def query(self) -> str:
17
+ return f"{self.OFFSET} {self._number}"
@@ -0,0 +1,29 @@
1
+ from typing import override, Callable
2
+
3
+ from ....utils.lambda_disassembler.tree_instruction import TreeInstruction
4
+ from ....common.interfaces.IQueryCommand import IQuery
5
+ from ....common.interfaces.IStatements import OrderType
6
+
7
+ ASC = "ASC"
8
+ DESC = "DESC"
9
+
10
+
11
+ class OrderQuery[T](IQuery):
12
+ ORDER = "ORDER BY"
13
+
14
+ def __init__(self, instance: T, order_lambda: Callable[[T], None], order_type: OrderType) -> None:
15
+ if not self._valid_order_type(order_type):
16
+ raise Exception("order_type only can be 'ASC' or 'DESC'")
17
+
18
+ self._instance: T = instance
19
+ self._order_lambda: Callable[[T], None] = order_lambda
20
+ self._order_type: str = order_type
21
+ self._column: str = TreeInstruction(order_lambda).to_list()[0].nested_element.name
22
+
23
+ def _valid_order_type(self, _value: str) -> bool:
24
+ return _value in (ASC, DESC)
25
+
26
+ @override
27
+ @property
28
+ def query(self) -> str:
29
+ return f"{self.ORDER} {self._instance.__table_name__}.{self._column} {self._order_type}"
@@ -0,0 +1,172 @@
1
+ from typing import Callable, Optional, Type, override
2
+ import inspect
3
+
4
+ from ....utils.lambda_disassembler import TreeInstruction, TupleInstruction, NestedElement
5
+ from ....components.select import ISelect, TableColumn
6
+ from ....utils import Table, ForeignKey
7
+ from ....utils.table_constructor import TableMeta
8
+
9
+ from . import JoinSelector, JoinType
10
+
11
+
12
+ class SelectQuery[T: Table, *Ts](ISelect):
13
+ SELECT = "SELECT"
14
+
15
+ def __init__(
16
+ self,
17
+ tables: T | tuple[T, *Ts] = (),
18
+ select_lambda: Optional[Callable[[T, *Ts], None]] = lambda: None,
19
+ *,
20
+ by: JoinType = JoinType.INNER_JOIN,
21
+ ) -> None:
22
+ if not isinstance(tables, tuple):
23
+ tables = tuple([tables])
24
+
25
+ self._first_table: T = tables[0]
26
+ self._tables: tuple[T, *Ts] = tables
27
+ self._select_lambda: Optional[Callable[[T, *Ts], None]] = select_lambda
28
+ self._by: JoinType = by
29
+
30
+ self._tables_heritage: list[tuple[Table, Table]] = []
31
+ self._lambda_var_to_table_dicc: dict[str, Table] = self._assign_lambda_variables_to_table(select_lambda)
32
+
33
+ self._select_list: list[TableColumn] = self._rename_recursive_column_list(select_lambda)
34
+
35
+ def _rename_recursive_column_list(self, _lambda: Optional[Callable[[T], None]]) -> list[TableColumn]:
36
+ """
37
+ Recursive function tu replace variable names by Select Query
38
+
39
+ lambda a: (a.pk_address, a.city.pk_city, a.city.country.pk_country)
40
+
41
+ >>> # convert lambda expression into list of values
42
+ >>> select_list = [
43
+ >>> "a.pk_address",
44
+ >>> "a.city",
45
+ >>> "a.city.pk_city",
46
+ >>> "a.city.country",
47
+ >>> "a.city.country.pk_country",
48
+ >>> ]
49
+ >>> result = _rename_recursive_column_list(select_list)
50
+ >>> print(result)
51
+ >>> # result = [
52
+ >>> # "address.pk_address"
53
+ >>> # "city.*"
54
+ >>> # "city.pk_city"
55
+ >>> # "country.*"
56
+ >>> # "country.pk_country"
57
+ ]
58
+ """
59
+ instruction_list: list[TupleInstruction] = TreeInstruction(_lambda).to_list()
60
+ column_list: list[TableColumn] = []
61
+
62
+ for ti in instruction_list:
63
+ obj = self._lambda_var_to_table_dicc[ti.var]
64
+
65
+ var = obj.__table_name__
66
+ new_nested = ti.nested_element.parents
67
+ new_nested[0] = var
68
+ ti = TupleInstruction(var, NestedElement(new_nested))
69
+ self._get_parents(obj, ti, column_list)
70
+ return column_list
71
+
72
+ def _get_parents(self, tbl_obj: Table, tuple_inst: TupleInstruction, column_list: list[TableColumn]) -> None:
73
+ if self._user_want_all_col(tbl_obj, tuple_inst):
74
+ column_list.extend(list(TableColumn.all_columns(tbl_obj)))
75
+ return None
76
+
77
+ # if the 'last_el' var is a property, we'll know the user will want retrieve a column of the same instance of the 'tbl_obj'. Otherwise the user will want to get a column of the other instance
78
+ last_el: str = tuple_inst.nested_element.name
79
+ if self._user_want_column_of_the_same_table(tbl_obj, tuple_inst):
80
+ return column_list.append(TableColumn(tbl_obj, last_el))
81
+
82
+ parents: list[str] = tuple_inst.nested_element.parents
83
+ first_el = parents[1]
84
+ new_ti = TupleInstruction(first_el, NestedElement[str](parents[1:])) # create new TupleInstruction from the second parent to the top
85
+ new_attr = self.get_attribute_of(tbl_obj, first_el) # could be Table or property
86
+
87
+ self._add_fk_relationship(tbl_obj, new_attr)
88
+ return self._get_parents(new_attr, new_ti, column_list)
89
+
90
+ def _add_fk_relationship(self, t1: Table, t2: Table) -> None:
91
+ tuple_ = tuple([t1, t2])
92
+ if tuple_ not in self._tables_heritage:
93
+ self._tables_heritage.append(tuple_)
94
+ return None
95
+
96
+ @staticmethod
97
+ def _user_want_all_col(tbl: Table, ti: TupleInstruction) -> bool:
98
+ """
99
+ if ti.nested_element.parents length is 1 says that the element is the table itself (table.*)
100
+ """
101
+ return issubclass(tbl.__class__, Table | TableMeta) and len(ti.nested_element.parents) == 1
102
+
103
+ def _user_want_column_of_the_same_table(self, table: Table, ti: TupleInstruction) -> bool:
104
+ last_el: str = ti.nested_element.name
105
+ first_el = ti.nested_element.parents[1]
106
+
107
+ table_attr = self.get_attribute_of(table, first_el)
108
+
109
+ return last_el in table.__dict__ and isinstance(table_attr, property)
110
+
111
+ @staticmethod
112
+ def get_attribute_of[TProp: Table](table: TProp, _value: str) -> Optional[TProp | property]:
113
+ try:
114
+ return getattr(table.__class__, _value)
115
+ except Exception:
116
+ return getattr(table, _value, None)
117
+
118
+ def _assign_lambda_variables_to_table(self, _lambda: Callable[[T], None]) -> dict[str, Type[Table]]:
119
+ """
120
+ return a dictionary with the lambda's parameters as keys and Type[Table] as the values
121
+
122
+
123
+ >>> res = _assign_lambda_variables_to_table(lambda a,ci,co: ...)
124
+ >>> print(res)
125
+ >>> # {
126
+ >>> # "a": Address,
127
+ >>> # "ci": City,
128
+ >>> # "co": Country,
129
+ >>> # }
130
+ """
131
+ lambda_vars = tuple(inspect.signature(_lambda).parameters)
132
+
133
+ dicc: dict[str, Table] = {}
134
+ for i in range(len(lambda_vars)):
135
+ dicc[lambda_vars[i]] = self._tables[i]
136
+ return dicc
137
+
138
+ def _convert_select_list(self) -> str:
139
+ self._select_list = self._select_list if self._select_list else tuple(TableColumn.all_columns(self._first_table))
140
+
141
+ return ", ".join(col.column for col in self._select_list)
142
+
143
+ @override
144
+ @property
145
+ def query(self) -> str:
146
+ select_str = self._convert_select_list()
147
+ query: str = f"{self.SELECT} {select_str} FROM {self._first_table.__table_name__}"
148
+
149
+ involved_tables = self.get_involved_tables()
150
+ if not involved_tables:
151
+ return query
152
+
153
+ sub_query: str = ""
154
+ for l_tbl, r_tbl in involved_tables:
155
+ join = JoinSelector(l_tbl, r_tbl, by=self._by, where=ForeignKey.MAPPED[l_tbl][r_tbl])
156
+ sub_query += f" {join.query}"
157
+
158
+ query += sub_query
159
+ return query
160
+
161
+ @override
162
+ @property
163
+ def select_list(self) -> list[TableColumn]:
164
+ return self._select_list
165
+
166
+ @override
167
+ @property
168
+ def tables_heritage(self) -> list[tuple[Table, Table]]:
169
+ return self._tables_heritage
170
+
171
+ def get_involved_tables(self) -> tuple[tuple[Table, Table]]:
172
+ return tuple(self._tables_heritage)
@@ -0,0 +1,52 @@
1
+ from typing import Type, override, Any
2
+ from mysql.connector import MySQLConnection
3
+
4
+ from ....components.update import UpdateQueryBase
5
+ from ....utils import Table, Column
6
+ from ....common.interfaces import IRepositoryBase
7
+ from .where_condition import WhereCondition
8
+
9
+
10
+ class UpdateQuery[T: Type[Table]](UpdateQueryBase[T, IRepositoryBase[MySQLConnection]]):
11
+ def __init__(self, model: T, repository: Any, where: list[WhereCondition]) -> None:
12
+ super().__init__(model, repository, where)
13
+
14
+ @override
15
+ @property
16
+ def CLAUSE(self) -> str:
17
+ return "UPDATE"
18
+
19
+ @override
20
+ def execute(self) -> None:
21
+ if self._where:
22
+ self._query += " " + WhereCondition.join_condition(*self._where)
23
+ return self._repository.execute_with_values(self._query, self._values)
24
+
25
+ @override
26
+ def update(self, dicc: Any | dict[str | property, Any]) -> None:
27
+ if not isinstance(dicc, dict):
28
+ raise TypeError
29
+
30
+ name_cols: list[str] = []
31
+
32
+ for col, value in dicc.items():
33
+ if isinstance(col, str):
34
+ string_col = col
35
+ else:
36
+ string_col = self._model.__properties_mapped__.get(col, None)
37
+ if not string_col:
38
+ raise KeyError(f"Class '{self._model.__name__}' has not {col} mapped.")
39
+ if self.__is_valid__(string_col, value):
40
+ name_cols.append(string_col)
41
+ self._values.append(value)
42
+
43
+ set_query: str = ",".join(["=".join([col, "%s"]) for col in name_cols])
44
+
45
+ self._query = f"{self.CLAUSE} {self._model.__table_name__} SET {set_query}"
46
+ return None
47
+
48
+ def __is_valid__(self, col: str, value: Any) -> bool:
49
+ instance_table: Table = self._model(**{col: value})
50
+
51
+ column: Column = getattr(instance_table, f"_{col}")
52
+ return not column.is_auto_generated
@@ -0,0 +1,68 @@
1
+ from typing import override, Any
2
+
3
+ from ....utils import Table
4
+ from ....components.upsert import UpsertQueryBase
5
+ from ....common.interfaces import IRepositoryBase
6
+ from mysql.connector import MySQLConnection
7
+
8
+ from .insert import InsertQuery
9
+
10
+
11
+ class UpsertQuery[T: Table](UpsertQueryBase[T, IRepositoryBase[MySQLConnection]]):
12
+ def __init__(self, model: T, repository: Any) -> None:
13
+ super().__init__(model, repository)
14
+
15
+ @override
16
+ @property
17
+ def CLAUSE(self) -> str:
18
+ return "ON DUPLICATE KEY UPDATE"
19
+
20
+ @override
21
+ def execute(self) -> None:
22
+ return self._repository.executemany_with_values(self._query, self._values)
23
+
24
+ @override
25
+ def upsert(self, instances: T | list[T]) -> None:
26
+ """
27
+ Esta funcion se enfoca para trabajar con listas, aunque el argumneto changes sea un unico diccionario.
28
+
29
+ Accedemos a la primera posicion de la lista 'changes[0]' porque en la query solo estamos poniendo marcadores de posicion, alias y nombres de columnas
30
+
31
+ EXAMPLE
32
+ ------
33
+
34
+ MySQL
35
+ -----
36
+
37
+ INSERT INTO NAME_TABLE(PK_COL,COL2)
38
+ VALUES
39
+ (1,'PABLO'),
40
+ (2,'MARINA') AS _val
41
+ ON DUPLICATE KEY UPDATE
42
+ COL2 = _val.COL2;
43
+
44
+ Python
45
+ -----
46
+
47
+ INSERT INTO NAME_TABLE(PK_COL,COL2)
48
+ VALUES (%s, %s') AS _val
49
+ ON DUPLICATE KEY UPDATE
50
+ COL2 = _val.COL2;
51
+
52
+ """
53
+ insert = InsertQuery[T](self._model, self._repository)
54
+ insert.insert(instances)
55
+
56
+ if isinstance(instances, Table):
57
+ instances = tuple([instances])
58
+ ALIAS = "VALUES"
59
+
60
+ cols = instances[0].get_columns()
61
+ pk_key = instances[0].get_pk().column_name
62
+
63
+ alternative = ", ".join([f"{col}={ALIAS}({col})" for col in cols if col != pk_key])
64
+ query = f"{insert._query} {self.CLAUSE} {alternative};"
65
+
66
+ self._query = query
67
+ self._values = insert.values
68
+ return None
@@ -0,0 +1,219 @@
1
+ from typing import Any, Callable, Optional, override
2
+ import inspect
3
+
4
+ from ....common.enums import ConditionType
5
+ from ....utils.lambda_disassembler.tree_instruction import TreeInstruction, TupleInstruction
6
+ from ....common.interfaces.IQueryCommand import IQuery
7
+ from ....components.where.abstract_where import AbstractWhere
8
+ from ....utils import Table
9
+
10
+
11
+ class WhereConditionByArg[TProp1, TProp2](IQuery):
12
+ def __init__(self, cond1: TProp1, cond2: TProp2, symbol: ConditionType) -> None:
13
+ self.cond1: TProp1 = cond1
14
+ self.cond2: TProp2 = cond2
15
+ self.symbol: ConditionType = symbol
16
+
17
+ @property
18
+ def query(self) -> str:
19
+ return f"WHERE {self.cond1} {self.symbol.value} {self.cond2}"
20
+
21
+
22
+ class WhereCondition[*Inst](AbstractWhere):
23
+ """
24
+ The purpose of this class is to create 'WHERE' condition queries properly.
25
+
26
+ Args.
27
+ - instances: tuple[*Inst],
28
+ - passed all instance that we are going to use inside of `function` arg
29
+
30
+ - function: Callable[[*Inst], bool] = lambda: None,
31
+ - lambda function to create condition between instance variables
32
+ - **kwargs: Any,
33
+ - We use this clause by passing all the variables that we want to replace inside the lambda function.
34
+ When we try to disassemble the lambda function, we see that the variables were not replaced by their values.
35
+ Instead, we only got the variable names, not the values.
36
+ Due to this problem, we need to specify the correct dictionary to map variable names to their values.
37
+
38
+ >>> var = 100
39
+ >>> _lambda = lambda a: a.city_id <= var
40
+ >>> ... #Dissamble _lambda method
41
+ >>> parts_of_lambda = [
42
+ >>> "city_id"
43
+ >>> "<="
44
+ >>> "var" <-------- We excepted 100
45
+ >>> ]
46
+ """
47
+
48
+ __slots__ = [
49
+ "_instances",
50
+ "_function",
51
+ "_tree",
52
+ "_kwargs",
53
+ "_lambda_param_map",
54
+ ]
55
+
56
+ def __init__(
57
+ self,
58
+ instances: tuple[*Inst],
59
+ function: Callable[[*Inst], bool] = lambda: None,
60
+ **kwargs: Any,
61
+ ) -> None:
62
+ self._instances: tuple[Table] = instances
63
+ self._function: Callable[[*Inst], bool] = function
64
+ self._kwargs: dict[str, tuple[*Inst]] = kwargs
65
+ self._tree: TreeInstruction = TreeInstruction(function)
66
+ self._lambda_param_map: dict[str, Table] = self._create_lambda_param_map()
67
+
68
+ def _create_lambda_param_map(self) -> dict[str, Table]:
69
+ """
70
+ The method is responsible for mapping the variables present in the lambda function so that they are replaced with the instance of the model Table.
71
+ """
72
+ assert len(lamda_param := inspect.signature(self._function).parameters) == len(self._instances)
73
+
74
+ _temp_instances = list(self._instances)[::-1] # we copied and translated tuple instance due to pop each value in order to param
75
+ new_dicc: dict[str, Table] = {}
76
+ for param in lamda_param.keys():
77
+ new_dicc[param] = _temp_instances.pop()
78
+
79
+ return new_dicc
80
+
81
+ @override
82
+ @property
83
+ def query(self) -> str:
84
+ if len(self._tree.compare_op) == 0:
85
+ return self._build_with_lambda_as_column_name()
86
+ return self._build_with_lambda_as_condition()
87
+
88
+ def _build_with_lambda_as_column_name(self) -> str:
89
+ conditions, compare_sign = self.create_conditions_list_and_compare_sign()
90
+ c1, c2 = conditions
91
+ return f"{self.WHERE} {c1} {compare_sign[0]} {c2}"
92
+
93
+ def _replace_values(self, ti: TupleInstruction) -> str:
94
+ instance: Any = self._kwargs[ti.var]
95
+ if isinstance(instance, Table):
96
+ data = getattr(instance, ti.nested_element.name)
97
+ else:
98
+ data = instance
99
+
100
+ return f"'{data}'" if isinstance(data, str) else data
101
+
102
+ def _build_with_lambda_as_condition(self) -> Callable[[], Any]:
103
+ n: int = len(self._tree.compare_op)
104
+ dicc_selector: dict[int, Callable[[], str]] = {
105
+ 1: self.__one_sign,
106
+ 2: self.__two_sign,
107
+ }
108
+ return dicc_selector[n]()
109
+
110
+ def __one_sign(self) -> str:
111
+ """lambda x: x <= 10"""
112
+ (c1, c2), _ = self.create_conditions_list_and_compare_sign()
113
+
114
+ return f"{self.WHERE} {c1} {self._tree.compare_op[0]} {c2}"
115
+
116
+ def _get_table_for_tuple_instruction(self, ti: TupleInstruction) -> Optional[Table]:
117
+ if ti.var not in self._lambda_param_map:
118
+ return None
119
+
120
+ involved_tables: list[Table] = [self._lambda_param_map[ti.var]]
121
+
122
+ def get_attr_tbl(tbl: Table, class_var: str) -> Optional[Table]:
123
+ tbl_attrs = (tbl, class_var)
124
+ if hasattr(*tbl_attrs):
125
+ attr = getattr(*tbl_attrs)
126
+
127
+ if not isinstance(attr, property) and issubclass(attr, Table):
128
+ return attr
129
+ return None
130
+
131
+ for name in ti.nested_element.parents[1:]:
132
+ attr = get_attr_tbl(involved_tables[-1], name)
133
+ if attr is not None:
134
+ involved_tables.append(attr)
135
+
136
+ return involved_tables[-1].__table_name__
137
+
138
+ def __two_sign(self) -> str:
139
+ """lambda x: 100 <= x <= 500"""
140
+ self.__valid_between_comparable_sign()
141
+ conds, _ = self.create_conditions_list_and_compare_sign()
142
+ c1, c2, c3 = conds
143
+ cond1 = WhereConditionByArg[str, str](c1, c2, ConditionType(self._tree.compare_op[0]))
144
+ cond2 = WhereConditionByArg[str, str](c2, c3, ConditionType(self._tree.compare_op[1]))
145
+
146
+ return WhereCondition.join_condition(cond1, cond2, restrictive=True)
147
+
148
+ def __valid_between_comparable_sign(self) -> bool:
149
+ if not len(self._tree.compare_op) == 2:
150
+ raise Exception("Number of comparable signs distinct from 2.")
151
+ return True
152
+
153
+ @classmethod
154
+ def join_condition(cls, *args: WhereConditionByArg, restrictive=False) -> str:
155
+ BY: str = "AND" if restrictive else "OR"
156
+ query: str = f"{cls.WHERE} "
157
+
158
+ n = len(args)
159
+ for i in range(n):
160
+ condition: IQuery = args[i]
161
+ query += "(" + condition.query.removeprefix(f"{cls.WHERE} ") + ")"
162
+ if i != n - 1:
163
+ query += f" {BY} "
164
+
165
+ return query
166
+
167
+ @override
168
+ def get_involved_tables(self) -> tuple[tuple[Table, Table]]:
169
+ return_involved_tables: list[tuple[Table, Table]] = []
170
+ involved_tables: list[Table] = [self._instances[0]]
171
+
172
+ def get_attr_tbl(instance: Table, tbl_name: str) -> Optional[Table]:
173
+ inst_tbl_name = (instance, tbl_name)
174
+ if hasattr(*inst_tbl_name):
175
+ attr = getattr(*inst_tbl_name)
176
+
177
+ if not isinstance(attr, property) and issubclass(attr, Table):
178
+ return attr
179
+ return None
180
+
181
+ tables: list[str] = self._tree.to_list()[0].nested_element.parents[1:] # Avoid lambda variable
182
+ for tbl_name in tables:
183
+ tbl = involved_tables[-1]
184
+ attr = get_attr_tbl(tbl, tbl_name)
185
+ if attr is not None:
186
+ involved_tables.append(attr)
187
+ return_involved_tables.append(tuple([tbl, attr]))
188
+ return tuple(return_involved_tables)
189
+
190
+ def create_conditions_list_and_compare_sign(self) -> tuple[list[str], list[str]]:
191
+ compare_sign: list[str] = []
192
+ conds: list[str] = []
193
+ for ti in self._tree.to_list():
194
+ key = ti.var
195
+ ne = ti.nested_element
196
+
197
+ if hasattr(ConditionType, str(ne.name)):
198
+ cond_type: ConditionType = getattr(ConditionType, ne.name)
199
+ compare_sign.append(cond_type.value)
200
+
201
+ elif key in self._kwargs:
202
+ conds.append(self._replace_values(ti))
203
+ else:
204
+ _name_table = self._get_table_for_tuple_instruction(ti)
205
+ _name_table_str = f"{_name_table}." if _name_table else ""
206
+
207
+ _name = ne.name
208
+ if not _name_table:
209
+ _name = self._wrapp_condition_id_str(ne.name)
210
+
211
+ conds.append(f"{_name_table_str}{_name}")
212
+ return conds, compare_sign
213
+
214
+ def _wrapp_condition_id_str(self, name: Any):
215
+ if not name:
216
+ return "NULL"
217
+ if not isinstance(name, str):
218
+ return name
219
+ return f"'{name}'"