pancakes-orm 3.0.0__tar.gz → 3.2.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.
- {pancakes_orm-3.0.0/pancakes_orm.egg-info → pancakes_orm-3.2.0}/PKG-INFO +2 -2
- {pancakes_orm-3.0.0 → pancakes_orm-3.2.0}/README.md +1 -1
- {pancakes_orm-3.0.0 → pancakes_orm-3.2.0}/pancakes/cook/layer.py +17 -2
- {pancakes_orm-3.0.0 → pancakes_orm-3.2.0}/pancakes/cook/mold.py +22 -6
- {pancakes_orm-3.0.0 → pancakes_orm-3.2.0}/pancakes/tool/box.py +164 -20
- {pancakes_orm-3.0.0 → pancakes_orm-3.2.0/pancakes_orm.egg-info}/PKG-INFO +2 -2
- {pancakes_orm-3.0.0 → pancakes_orm-3.2.0}/pyproject.toml +1 -1
- {pancakes_orm-3.0.0 → pancakes_orm-3.2.0}/LICENSE +0 -0
- {pancakes_orm-3.0.0 → pancakes_orm-3.2.0}/MANIFEST.in +0 -0
- {pancakes_orm-3.0.0 → pancakes_orm-3.2.0}/pancakes/__init__.py +0 -0
- {pancakes_orm-3.0.0 → pancakes_orm-3.2.0}/pancakes/cook/__init__.py +0 -0
- {pancakes_orm-3.0.0 → pancakes_orm-3.2.0}/pancakes/cook/clean.py +0 -0
- {pancakes_orm-3.0.0 → pancakes_orm-3.2.0}/pancakes/cook/furnace.py +0 -0
- {pancakes_orm-3.0.0 → pancakes_orm-3.2.0}/pancakes/cook/ingredient.py +0 -0
- {pancakes_orm-3.0.0 → pancakes_orm-3.2.0}/pancakes/datatype/__init__.py +0 -0
- {pancakes_orm-3.0.0 → pancakes_orm-3.2.0}/pancakes/datatype/sql_datatype.py +0 -0
- {pancakes_orm-3.0.0 → pancakes_orm-3.2.0}/pancakes/tool/__init__.py +0 -0
- {pancakes_orm-3.0.0 → pancakes_orm-3.2.0}/pancakes/tool/function.py +0 -0
- {pancakes_orm-3.0.0 → pancakes_orm-3.2.0}/pancakes/tool/idu.py +0 -0
- {pancakes_orm-3.0.0 → pancakes_orm-3.2.0}/pancakes_orm.egg-info/SOURCES.txt +0 -0
- {pancakes_orm-3.0.0 → pancakes_orm-3.2.0}/pancakes_orm.egg-info/dependency_links.txt +0 -0
- {pancakes_orm-3.0.0 → pancakes_orm-3.2.0}/pancakes_orm.egg-info/requires.txt +0 -0
- {pancakes_orm-3.0.0 → pancakes_orm-3.2.0}/pancakes_orm.egg-info/top_level.txt +0 -0
- {pancakes_orm-3.0.0 → pancakes_orm-3.2.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pancakes-orm
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.2.0
|
|
4
4
|
Summary: Un ORM simple, potente y flexible llamado PanCakes
|
|
5
5
|
Author-email: Andres Alberto Lopez Mendoza <andres.lopez.full.dev@gmail.com>
|
|
6
6
|
License: Apache-2.0
|
|
@@ -79,7 +79,7 @@ PanCakesORM no es solo una cara bonita; está construido para ser el motor confi
|
|
|
79
79
|
Streamlit: Ideal para aplicaciones de datos donde la velocidad de desarrollo es clave.
|
|
80
80
|
|
|
81
81
|
Calidad Garantizada: La robustez de la librería está respaldada por una batería
|
|
82
|
-
de más de
|
|
82
|
+
de más de 125 pruebas automatizadas utilizando pytest. Puedes consultar la suite completa en la carpeta /tests.
|
|
83
83
|
|
|
84
84
|
🏗️ Arquitectura de Consultas (QueryBox)
|
|
85
85
|
|
|
@@ -61,7 +61,7 @@ PanCakesORM no es solo una cara bonita; está construido para ser el motor confi
|
|
|
61
61
|
Streamlit: Ideal para aplicaciones de datos donde la velocidad de desarrollo es clave.
|
|
62
62
|
|
|
63
63
|
Calidad Garantizada: La robustez de la librería está respaldada por una batería
|
|
64
|
-
de más de
|
|
64
|
+
de más de 125 pruebas automatizadas utilizando pytest. Puedes consultar la suite completa en la carpeta /tests.
|
|
65
65
|
|
|
66
66
|
🏗️ Arquitectura de Consultas (QueryBox)
|
|
67
67
|
|
|
@@ -40,7 +40,8 @@ def query(
|
|
|
40
40
|
condition: list = None,
|
|
41
41
|
group_by: list = None,
|
|
42
42
|
order_by: list = None,
|
|
43
|
-
limit: int = None
|
|
43
|
+
limit: int = None,
|
|
44
|
+
offset: int = None
|
|
44
45
|
):
|
|
45
46
|
"""
|
|
46
47
|
Ejecuta una consulta SQL avanzada, sanitizada y dinámica.
|
|
@@ -482,6 +483,19 @@ def query(
|
|
|
482
483
|
limit_clause = "LIMIT ?"
|
|
483
484
|
c_data.append(limit)
|
|
484
485
|
|
|
486
|
+
# CONSTRUCCIÓN 'OFFSET' CLAUSE
|
|
487
|
+
offset_clause = ""
|
|
488
|
+
|
|
489
|
+
if offset:
|
|
490
|
+
|
|
491
|
+
if not isinstance(offset, int):
|
|
492
|
+
msg = f"Invalid datatype: {offset}."
|
|
493
|
+
logger.critical(msg)
|
|
494
|
+
raise TypeError(type(offset))
|
|
495
|
+
|
|
496
|
+
offset_clause = "OFFSET ?"
|
|
497
|
+
c_data.append(offset)
|
|
498
|
+
|
|
485
499
|
# Construccion del Query Final
|
|
486
500
|
sql = (
|
|
487
501
|
f"{select_clause} "
|
|
@@ -489,7 +503,8 @@ def query(
|
|
|
489
503
|
f"{where_clause} "
|
|
490
504
|
f"{group_clause} "
|
|
491
505
|
f"{order_clause} "
|
|
492
|
-
f"{limit_clause}
|
|
506
|
+
f"{limit_clause} "
|
|
507
|
+
f"{offset_clause};"
|
|
493
508
|
)
|
|
494
509
|
|
|
495
510
|
sql = " ".join(sql.split()).strip()
|
|
@@ -103,7 +103,6 @@ class PanCakesORM:
|
|
|
103
103
|
_family = {}
|
|
104
104
|
_db_dir = DEFAULT_DIR
|
|
105
105
|
_db_file = DEFAULT_DB_FILE
|
|
106
|
-
_depends = "self"
|
|
107
106
|
_metadata = {}
|
|
108
107
|
_order = []
|
|
109
108
|
|
|
@@ -176,7 +175,7 @@ class PanCakesORM:
|
|
|
176
175
|
@classmethod
|
|
177
176
|
def _check_dependencies(cls) -> None:
|
|
178
177
|
"""
|
|
179
|
-
Evalua el atributo de clase cls.
|
|
178
|
+
Evalua el atributo de clase cls._depends:
|
|
180
179
|
1. Valida el tipo de dato
|
|
181
180
|
2. Valida: O string valido o iterable valido.
|
|
182
181
|
3. Se guardan las dependencias en una lista de diccionarios.
|
|
@@ -185,6 +184,16 @@ class PanCakesORM:
|
|
|
185
184
|
|
|
186
185
|
externals = []
|
|
187
186
|
|
|
187
|
+
if '_depends' not in cls.__dict__.keys():
|
|
188
|
+
msg = (
|
|
189
|
+
f"Model {cls._table} does not have declared attribute "
|
|
190
|
+
f"'_depends' which must be specified for relations "
|
|
191
|
+
f"among tables in database. PanCakesORM set _'depends' "
|
|
192
|
+
f"to 'self'; table - {cls._table}"
|
|
193
|
+
)
|
|
194
|
+
logger.warning(msg)
|
|
195
|
+
cls._depends = "self"
|
|
196
|
+
|
|
188
197
|
# Validar tipo de dato
|
|
189
198
|
if not isinstance(cls._depends, (str, list, tuple)):
|
|
190
199
|
msg = (
|
|
@@ -199,8 +208,8 @@ class PanCakesORM:
|
|
|
199
208
|
if isinstance(cls._depends, str) and cls._depends != "self":
|
|
200
209
|
msg = (
|
|
201
210
|
f"Class attribute _depends only accepts the "
|
|
202
|
-
f"following string when there
|
|
203
|
-
f"
|
|
211
|
+
f"following string when there are no dependencies "
|
|
212
|
+
f"to other tables: 'self'. Any other string will "
|
|
204
213
|
f"raise this message."
|
|
205
214
|
)
|
|
206
215
|
logger.critical(msg)
|
|
@@ -490,7 +499,8 @@ class PanCakesORM:
|
|
|
490
499
|
condition: list = None,
|
|
491
500
|
group_by: list = None,
|
|
492
501
|
order_by: list = None,
|
|
493
|
-
limit: int = None
|
|
502
|
+
limit: int = None,
|
|
503
|
+
offset: int = None
|
|
494
504
|
):
|
|
495
505
|
"""
|
|
496
506
|
Llama a la funcion de consulta global.
|
|
@@ -505,6 +515,7 @@ class PanCakesORM:
|
|
|
505
515
|
group_by = None if group_by is None else group_by
|
|
506
516
|
order_by = None if order_by is None else order_by
|
|
507
517
|
limit = None if limit is None else limit
|
|
518
|
+
offset = None if offset is None else offset
|
|
508
519
|
|
|
509
520
|
res, col = query(
|
|
510
521
|
db_path=db_path,
|
|
@@ -515,7 +526,8 @@ class PanCakesORM:
|
|
|
515
526
|
condition=condition,
|
|
516
527
|
group_by=group_by,
|
|
517
528
|
order_by=order_by,
|
|
518
|
-
limit=limit
|
|
529
|
+
limit=limit,
|
|
530
|
+
offset=offset
|
|
519
531
|
)
|
|
520
532
|
|
|
521
533
|
return res, col
|
|
@@ -617,6 +629,10 @@ class PanCakesORM:
|
|
|
617
629
|
def lim(cls, num: int = None):
|
|
618
630
|
return cls.q().lim(num)
|
|
619
631
|
|
|
632
|
+
@classmethod # Inyección Segura
|
|
633
|
+
def off(cls, num: int = None):
|
|
634
|
+
return cls.q().off(num)
|
|
635
|
+
|
|
620
636
|
@classmethod # Inyeccion Segura
|
|
621
637
|
def all(cls):
|
|
622
638
|
return cls.q().all()
|
|
@@ -51,6 +51,7 @@ class QueryBox:
|
|
|
51
51
|
self.group = None
|
|
52
52
|
self.order = None
|
|
53
53
|
self.limit = None
|
|
54
|
+
self.offset = None
|
|
54
55
|
self.ids = False
|
|
55
56
|
self.row = None
|
|
56
57
|
self.col = None
|
|
@@ -385,6 +386,10 @@ class QueryBox:
|
|
|
385
386
|
return
|
|
386
387
|
|
|
387
388
|
def select(self, *columns):
|
|
389
|
+
|
|
390
|
+
if not columns:
|
|
391
|
+
return self
|
|
392
|
+
|
|
388
393
|
AGGS = {
|
|
389
394
|
"min": 'MIN',
|
|
390
395
|
"max": 'MAX',
|
|
@@ -459,17 +464,35 @@ class QueryBox:
|
|
|
459
464
|
self.sp_select.append(dicc)
|
|
460
465
|
continue
|
|
461
466
|
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
+
elif tab == m_tab:
|
|
468
|
+
# Obtenemos el comment desde la tabla main
|
|
469
|
+
lab = [c.comment for c in self.model._fields if c._name == col]
|
|
470
|
+
lab = f"{"".join(lab)} {agg.upper()}"
|
|
471
|
+
lab = " ".join(lab.split(" ")).strip()
|
|
472
|
+
self.s_label.append(lab)
|
|
467
473
|
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
474
|
+
dicc = {
|
|
475
|
+
"name": col,
|
|
476
|
+
"agg": agg
|
|
477
|
+
}
|
|
478
|
+
self.s_select.append(dicc)
|
|
479
|
+
continue
|
|
480
|
+
|
|
481
|
+
# Validar; no s_select, si sp_select
|
|
482
|
+
if not self.s_select:
|
|
483
|
+
m_tab = self.model._table
|
|
484
|
+
name = self.model._table
|
|
485
|
+
if "_" in name:
|
|
486
|
+
name = name.split("_")
|
|
487
|
+
name = " ".join(name)
|
|
488
|
+
|
|
489
|
+
self.s_label.append(f"{name} id".upper())
|
|
490
|
+
self.s_select = [
|
|
491
|
+
{
|
|
492
|
+
"name": f"{m_tab}_id",
|
|
493
|
+
"agg": ""
|
|
494
|
+
}
|
|
495
|
+
]
|
|
473
496
|
|
|
474
497
|
return self
|
|
475
498
|
|
|
@@ -826,6 +849,7 @@ class QueryBox:
|
|
|
826
849
|
|
|
827
850
|
def sort(self, **kwargs):
|
|
828
851
|
"""
|
|
852
|
+
Equivalente a ORDER BY
|
|
829
853
|
"""
|
|
830
854
|
DIRECTION = {'DESC', 'ASC', ''}
|
|
831
855
|
|
|
@@ -887,6 +911,20 @@ class QueryBox:
|
|
|
887
911
|
self.limit = limit
|
|
888
912
|
return self
|
|
889
913
|
|
|
914
|
+
def off(self, offset: int):
|
|
915
|
+
"""
|
|
916
|
+
Asigna un valor integer para seleccionar
|
|
917
|
+
el comienzo del query.
|
|
918
|
+
"""
|
|
919
|
+
if not isinstance(offset, int):
|
|
920
|
+
msg = f"Invalid datatype: {offset}."
|
|
921
|
+
logger.critical(msg)
|
|
922
|
+
raise TypeError(type(offset))
|
|
923
|
+
|
|
924
|
+
offset = offset if offset else None
|
|
925
|
+
self.offset = offset
|
|
926
|
+
return self
|
|
927
|
+
|
|
890
928
|
def id(self):
|
|
891
929
|
"""
|
|
892
930
|
Bandera que especifica al query devolver
|
|
@@ -913,6 +951,7 @@ class QueryBox:
|
|
|
913
951
|
group = self.group if self.group else None
|
|
914
952
|
order = self.order if self.order else None
|
|
915
953
|
limit = self.limit if self.limit else None
|
|
954
|
+
offset = self.offset if self.offset else None
|
|
916
955
|
|
|
917
956
|
ambiguous = (
|
|
918
957
|
ids and s_select,
|
|
@@ -934,8 +973,7 @@ class QueryBox:
|
|
|
934
973
|
sp_select = None
|
|
935
974
|
|
|
936
975
|
# Evaluar cuando .all() no contiene select():
|
|
937
|
-
|
|
938
|
-
if not s_select:
|
|
976
|
+
if s_select is None and sp_select is None:
|
|
939
977
|
self._if_no_select()
|
|
940
978
|
self.all()
|
|
941
979
|
return self
|
|
@@ -949,7 +987,8 @@ class QueryBox:
|
|
|
949
987
|
condition=condition,
|
|
950
988
|
group_by=group,
|
|
951
989
|
order_by=order,
|
|
952
|
-
limit=limit
|
|
990
|
+
limit=limit,
|
|
991
|
+
offset=offset
|
|
953
992
|
)
|
|
954
993
|
|
|
955
994
|
self.row = row
|
|
@@ -958,15 +997,15 @@ class QueryBox:
|
|
|
958
997
|
return self
|
|
959
998
|
|
|
960
999
|
def link(self, *relation):
|
|
1000
|
+
""" Esta función imita **kwargs de add()
|
|
1001
|
+
tipo de union + __ + tabla extra = [id referencia, tabla]
|
|
1002
|
+
"""
|
|
961
1003
|
|
|
962
1004
|
# Si no hay datos, salir
|
|
963
1005
|
if not relation:
|
|
964
1006
|
return self
|
|
965
1007
|
|
|
966
|
-
|
|
967
|
-
# Imitan el **kwargs de .add()
|
|
968
|
-
# tipo de union + __ + tabla extra = [id referencia, tabla]
|
|
969
|
-
|
|
1008
|
+
by_dependency = False
|
|
970
1009
|
kwargs = {}
|
|
971
1010
|
|
|
972
1011
|
# Iteracion sobre los nombre de tablas dadas
|
|
@@ -1033,10 +1072,115 @@ class QueryBox:
|
|
|
1033
1072
|
# Por defecto INNER JOIN
|
|
1034
1073
|
kwargs[f"in__{rel}"] = [field, self.model._table]
|
|
1035
1074
|
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1075
|
+
dicc_values = kwargs.values()
|
|
1076
|
+
for v in dicc_values:
|
|
1077
|
+
if v[0] is None or v[1] is None:
|
|
1078
|
+
by_dependency = True
|
|
1079
|
+
break
|
|
1080
|
+
|
|
1081
|
+
if by_dependency:
|
|
1082
|
+
copy = list(relation)
|
|
1083
|
+
|
|
1084
|
+
# Orden de relacion por dependencia:
|
|
1085
|
+
DEP = reversed(self.model._order)
|
|
1086
|
+
copy.insert(0, self.model._table)
|
|
1087
|
+
REL = [d for d in DEP if d in copy]
|
|
1088
|
+
winner = self.model
|
|
1089
|
+
|
|
1090
|
+
# Iterar esquema de relación
|
|
1091
|
+
kwargs = {}
|
|
1092
|
+
for t in REL:
|
|
1093
|
+
|
|
1094
|
+
# Bandera Validación
|
|
1095
|
+
flag = True
|
|
1096
|
+
|
|
1097
|
+
TABS = REL
|
|
1098
|
+
MODEL = self.model._family[t]
|
|
1099
|
+
TABS.remove(MODEL._table)
|
|
1100
|
+
|
|
1101
|
+
# Iteracion sobre los nombre de tablas dadas
|
|
1102
|
+
for rel in TABS:
|
|
1103
|
+
|
|
1104
|
+
# validar que se hayan pasado strings
|
|
1105
|
+
if not isinstance(rel, str):
|
|
1106
|
+
msg = (
|
|
1107
|
+
f"Passed table name {rel} "
|
|
1108
|
+
f"must be a string."
|
|
1109
|
+
)
|
|
1110
|
+
logger.critical(msg)
|
|
1111
|
+
raise TypeError(type(rel))
|
|
1112
|
+
|
|
1113
|
+
field = None
|
|
1114
|
+
|
|
1115
|
+
# Iteramos los "objetos" tipo "campos" <- o sea columnas
|
|
1116
|
+
# "_fields"
|
|
1117
|
+
for f in MODEL._fields:
|
|
1118
|
+
|
|
1119
|
+
# Evaluamos que el campo tenga el atributo:
|
|
1120
|
+
# "second_table".
|
|
1121
|
+
# Que el nombre dado y que el nombre del campo
|
|
1122
|
+
# sean iguales
|
|
1123
|
+
#
|
|
1124
|
+
# Se guarda y salimos de bucle
|
|
1125
|
+
if (
|
|
1126
|
+
hasattr(f, "second_table") and
|
|
1127
|
+
rel == f.second_table
|
|
1128
|
+
):
|
|
1129
|
+
|
|
1130
|
+
field = f._name
|
|
1131
|
+
winner = MODEL
|
|
1132
|
+
break
|
|
1133
|
+
|
|
1134
|
+
# Validamos que la operacion directa no encontro campos
|
|
1135
|
+
# Pasamos a la operacion inversa (de tabla padre a hija).
|
|
1136
|
+
if field is None:
|
|
1137
|
+
|
|
1138
|
+
# Buscamos relacion del PRIMARY KEY de la tabla
|
|
1139
|
+
# A la FOREIGN KEY de la tabla hija
|
|
1140
|
+
c_id = f"{MODEL._table}_id"
|
|
1141
|
+
# Buscamos que la tabla exista en la base de datos
|
|
1142
|
+
if rel not in MODEL._family:
|
|
1143
|
+
msg = (
|
|
1144
|
+
f"Passed {rel} tables does not exists. "
|
|
1145
|
+
"Make sure of the spelling and "
|
|
1146
|
+
"make sure of relation before "
|
|
1147
|
+
"trying again."
|
|
1148
|
+
)
|
|
1149
|
+
logger.critical(msg)
|
|
1150
|
+
raise KeyError(rel)
|
|
1151
|
+
|
|
1152
|
+
# Si existe accedemos a la tabla entera
|
|
1153
|
+
# e iteramos sus campos
|
|
1154
|
+
tab2 = MODEL._family[rel]
|
|
1155
|
+
|
|
1156
|
+
for f in tab2._fields:
|
|
1157
|
+
if c_id == f._name:
|
|
1158
|
+
field = f._name
|
|
1159
|
+
winner = MODEL
|
|
1160
|
+
break
|
|
1161
|
+
|
|
1162
|
+
# Si no relación absoluta; reportar cambio de esquema
|
|
1163
|
+
if field is None:
|
|
1164
|
+
flag = False
|
|
1165
|
+
break
|
|
1166
|
+
|
|
1167
|
+
# Agregamos la relacion al dicc "kwargs"
|
|
1168
|
+
# Por defecto INNER JOIN
|
|
1169
|
+
kwargs[f"in__{rel}"] = [field, MODEL._table]
|
|
1170
|
+
|
|
1171
|
+
# Cambiamos el esquema de busqueda
|
|
1172
|
+
if flag is False:
|
|
1173
|
+
continue
|
|
1174
|
+
else:
|
|
1175
|
+
break
|
|
1176
|
+
|
|
1177
|
+
# Si se encontraron nulos:
|
|
1178
|
+
self.model = winner
|
|
1179
|
+
self.add(**kwargs)
|
|
1180
|
+
return self
|
|
1039
1181
|
|
|
1182
|
+
# Si no hay nulos retornamos
|
|
1183
|
+
self.add(**kwargs)
|
|
1040
1184
|
return self
|
|
1041
1185
|
|
|
1042
1186
|
def count(self):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pancakes-orm
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.2.0
|
|
4
4
|
Summary: Un ORM simple, potente y flexible llamado PanCakes
|
|
5
5
|
Author-email: Andres Alberto Lopez Mendoza <andres.lopez.full.dev@gmail.com>
|
|
6
6
|
License: Apache-2.0
|
|
@@ -79,7 +79,7 @@ PanCakesORM no es solo una cara bonita; está construido para ser el motor confi
|
|
|
79
79
|
Streamlit: Ideal para aplicaciones de datos donde la velocidad de desarrollo es clave.
|
|
80
80
|
|
|
81
81
|
Calidad Garantizada: La robustez de la librería está respaldada por una batería
|
|
82
|
-
de más de
|
|
82
|
+
de más de 125 pruebas automatizadas utilizando pytest. Puedes consultar la suite completa en la carpeta /tests.
|
|
83
83
|
|
|
84
84
|
🏗️ Arquitectura de Consultas (QueryBox)
|
|
85
85
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|