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.
Files changed (24) hide show
  1. {pancakes_orm-3.0.0/pancakes_orm.egg-info → pancakes_orm-3.2.0}/PKG-INFO +2 -2
  2. {pancakes_orm-3.0.0 → pancakes_orm-3.2.0}/README.md +1 -1
  3. {pancakes_orm-3.0.0 → pancakes_orm-3.2.0}/pancakes/cook/layer.py +17 -2
  4. {pancakes_orm-3.0.0 → pancakes_orm-3.2.0}/pancakes/cook/mold.py +22 -6
  5. {pancakes_orm-3.0.0 → pancakes_orm-3.2.0}/pancakes/tool/box.py +164 -20
  6. {pancakes_orm-3.0.0 → pancakes_orm-3.2.0/pancakes_orm.egg-info}/PKG-INFO +2 -2
  7. {pancakes_orm-3.0.0 → pancakes_orm-3.2.0}/pyproject.toml +1 -1
  8. {pancakes_orm-3.0.0 → pancakes_orm-3.2.0}/LICENSE +0 -0
  9. {pancakes_orm-3.0.0 → pancakes_orm-3.2.0}/MANIFEST.in +0 -0
  10. {pancakes_orm-3.0.0 → pancakes_orm-3.2.0}/pancakes/__init__.py +0 -0
  11. {pancakes_orm-3.0.0 → pancakes_orm-3.2.0}/pancakes/cook/__init__.py +0 -0
  12. {pancakes_orm-3.0.0 → pancakes_orm-3.2.0}/pancakes/cook/clean.py +0 -0
  13. {pancakes_orm-3.0.0 → pancakes_orm-3.2.0}/pancakes/cook/furnace.py +0 -0
  14. {pancakes_orm-3.0.0 → pancakes_orm-3.2.0}/pancakes/cook/ingredient.py +0 -0
  15. {pancakes_orm-3.0.0 → pancakes_orm-3.2.0}/pancakes/datatype/__init__.py +0 -0
  16. {pancakes_orm-3.0.0 → pancakes_orm-3.2.0}/pancakes/datatype/sql_datatype.py +0 -0
  17. {pancakes_orm-3.0.0 → pancakes_orm-3.2.0}/pancakes/tool/__init__.py +0 -0
  18. {pancakes_orm-3.0.0 → pancakes_orm-3.2.0}/pancakes/tool/function.py +0 -0
  19. {pancakes_orm-3.0.0 → pancakes_orm-3.2.0}/pancakes/tool/idu.py +0 -0
  20. {pancakes_orm-3.0.0 → pancakes_orm-3.2.0}/pancakes_orm.egg-info/SOURCES.txt +0 -0
  21. {pancakes_orm-3.0.0 → pancakes_orm-3.2.0}/pancakes_orm.egg-info/dependency_links.txt +0 -0
  22. {pancakes_orm-3.0.0 → pancakes_orm-3.2.0}/pancakes_orm.egg-info/requires.txt +0 -0
  23. {pancakes_orm-3.0.0 → pancakes_orm-3.2.0}/pancakes_orm.egg-info/top_level.txt +0 -0
  24. {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.0.0
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 120 pruebas automatizadas utilizando pytest. Puedes consultar la suite completa en la carpeta /tests.
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 120 pruebas automatizadas utilizando pytest. Puedes consultar la suite completa en la carpeta /tests.
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.depends:
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 is no dependencies "
203
- f"on other tables: 'self'. Any other string will "
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
- # Obtenemos el comment desde la tabla main
463
- lab = [c.comment for c in self.model._fields if c._name == col]
464
- lab = f"{"".join(lab)} {agg.upper()}"
465
- lab = " ".join(lab.split(" ")).strip()
466
- self.s_label.append(lab)
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
- dicc = {
469
- "name": col,
470
- "agg": agg
471
- }
472
- self.s_select.append(dicc)
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
- # Obtener las relaciones en una lista de diccionarios
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
- # Ejecutamos la union
1037
- # Desempaquetamos el diccionario:
1038
- self.add(**kwargs)
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.0.0
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 120 pruebas automatizadas utilizando pytest. Puedes consultar la suite completa en la carpeta /tests.
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
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "pancakes-orm"
7
- version = "3.0.0"
7
+ version = "3.2.0"
8
8
  description = "Un ORM simple, potente y flexible llamado PanCakes"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.12"
File without changes
File without changes
File without changes