fastapi-basekit 0.1.24__tar.gz → 0.1.25__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.
- {fastapi_basekit-0.1.24 → fastapi_basekit-0.1.25}/PKG-INFO +1 -1
- {fastapi_basekit-0.1.24 → fastapi_basekit-0.1.25}/fastapi_basekit/aio/sqlalchemy/controller/base.py +0 -1
- {fastapi_basekit-0.1.24 → fastapi_basekit-0.1.25}/fastapi_basekit/aio/sqlalchemy/repository/base.py +107 -104
- {fastapi_basekit-0.1.24 → fastapi_basekit-0.1.25}/fastapi_basekit/aio/sqlalchemy/service/base.py +60 -39
- {fastapi_basekit-0.1.24 → fastapi_basekit-0.1.25}/fastapi_basekit.egg-info/PKG-INFO +1 -1
- {fastapi_basekit-0.1.24 → fastapi_basekit-0.1.25}/pyproject.toml +1 -1
- {fastapi_basekit-0.1.24 → fastapi_basekit-0.1.25}/LICENSE +0 -0
- {fastapi_basekit-0.1.24 → fastapi_basekit-0.1.25}/README.md +0 -0
- {fastapi_basekit-0.1.24 → fastapi_basekit-0.1.25}/fastapi_basekit/__init__.py +0 -0
- {fastapi_basekit-0.1.24 → fastapi_basekit-0.1.25}/fastapi_basekit/aio/__init__.py +0 -0
- {fastapi_basekit-0.1.24 → fastapi_basekit-0.1.25}/fastapi_basekit/aio/beanie/__init__.py +0 -0
- {fastapi_basekit-0.1.24 → fastapi_basekit-0.1.25}/fastapi_basekit/aio/beanie/controller/__init__.py +0 -0
- {fastapi_basekit-0.1.24 → fastapi_basekit-0.1.25}/fastapi_basekit/aio/beanie/controller/base.py +0 -0
- {fastapi_basekit-0.1.24 → fastapi_basekit-0.1.25}/fastapi_basekit/aio/beanie/repository/__init__.py +0 -0
- {fastapi_basekit-0.1.24 → fastapi_basekit-0.1.25}/fastapi_basekit/aio/beanie/repository/base.py +0 -0
- {fastapi_basekit-0.1.24 → fastapi_basekit-0.1.25}/fastapi_basekit/aio/beanie/service/__init__.py +0 -0
- {fastapi_basekit-0.1.24 → fastapi_basekit-0.1.25}/fastapi_basekit/aio/beanie/service/base.py +0 -0
- {fastapi_basekit-0.1.24 → fastapi_basekit-0.1.25}/fastapi_basekit/aio/controller/__init__.py +0 -0
- {fastapi_basekit-0.1.24 → fastapi_basekit-0.1.25}/fastapi_basekit/aio/controller/base.py +0 -0
- {fastapi_basekit-0.1.24 → fastapi_basekit-0.1.25}/fastapi_basekit/aio/permissions/__init__.py +0 -0
- {fastapi_basekit-0.1.24 → fastapi_basekit-0.1.25}/fastapi_basekit/aio/permissions/base.py +0 -0
- {fastapi_basekit-0.1.24 → fastapi_basekit-0.1.25}/fastapi_basekit/aio/sqlalchemy/__init__.py +0 -0
- {fastapi_basekit-0.1.24 → fastapi_basekit-0.1.25}/fastapi_basekit/aio/sqlalchemy/controller/__init__.py +0 -0
- {fastapi_basekit-0.1.24 → fastapi_basekit-0.1.25}/fastapi_basekit/aio/sqlalchemy/repository/__init__.py +0 -0
- {fastapi_basekit-0.1.24 → fastapi_basekit-0.1.25}/fastapi_basekit/aio/sqlalchemy/service/__init__.py +0 -0
- {fastapi_basekit-0.1.24 → fastapi_basekit-0.1.25}/fastapi_basekit/exceptions/__init__.py +0 -0
- {fastapi_basekit-0.1.24 → fastapi_basekit-0.1.25}/fastapi_basekit/exceptions/api_exceptions.py +0 -0
- {fastapi_basekit-0.1.24 → fastapi_basekit-0.1.25}/fastapi_basekit/exceptions/handler.py +0 -0
- {fastapi_basekit-0.1.24 → fastapi_basekit-0.1.25}/fastapi_basekit/schema/__init__.py +0 -0
- {fastapi_basekit-0.1.24 → fastapi_basekit-0.1.25}/fastapi_basekit/schema/base.py +0 -0
- {fastapi_basekit-0.1.24 → fastapi_basekit-0.1.25}/fastapi_basekit/schema/jwt.py +0 -0
- {fastapi_basekit-0.1.24 → fastapi_basekit-0.1.25}/fastapi_basekit/schema/schema.py +0 -0
- {fastapi_basekit-0.1.24 → fastapi_basekit-0.1.25}/fastapi_basekit/servicios/__init__.py +0 -0
- {fastapi_basekit-0.1.24 → fastapi_basekit-0.1.25}/fastapi_basekit/servicios/thrid/__init__.py +0 -0
- {fastapi_basekit-0.1.24 → fastapi_basekit-0.1.25}/fastapi_basekit/servicios/thrid/jwt.py +0 -0
- {fastapi_basekit-0.1.24 → fastapi_basekit-0.1.25}/fastapi_basekit.egg-info/SOURCES.txt +0 -0
- {fastapi_basekit-0.1.24 → fastapi_basekit-0.1.25}/fastapi_basekit.egg-info/dependency_links.txt +0 -0
- {fastapi_basekit-0.1.24 → fastapi_basekit-0.1.25}/fastapi_basekit.egg-info/requires.txt +0 -0
- {fastapi_basekit-0.1.24 → fastapi_basekit-0.1.25}/fastapi_basekit.egg-info/top_level.txt +0 -0
- {fastapi_basekit-0.1.24 → fastapi_basekit-0.1.25}/setup.cfg +0 -0
- {fastapi_basekit-0.1.24 → fastapi_basekit-0.1.25}/tests/test_api_exceptions.py +0 -0
- {fastapi_basekit-0.1.24 → fastapi_basekit-0.1.25}/tests/test_base_response.py +0 -0
- {fastapi_basekit-0.1.24 → fastapi_basekit-0.1.25}/tests/test_base_service.py +0 -0
- {fastapi_basekit-0.1.24 → fastapi_basekit-0.1.25}/tests/test_controller_auto_permissions.py +0 -0
- {fastapi_basekit-0.1.24 → fastapi_basekit-0.1.25}/tests/test_crud_beanie_controller.py +0 -0
- {fastapi_basekit-0.1.24 → fastapi_basekit-0.1.25}/tests/test_crud_controller.py +0 -0
- {fastapi_basekit-0.1.24 → fastapi_basekit-0.1.25}/tests/test_jwt_service.py +0 -0
{fastapi_basekit-0.1.24 → fastapi_basekit-0.1.25}/fastapi_basekit/aio/sqlalchemy/repository/base.py
RENAMED
|
@@ -16,10 +16,12 @@ class BaseRepository:
|
|
|
16
16
|
"""
|
|
17
17
|
|
|
18
18
|
model: Type[Any]
|
|
19
|
+
service: Optional[Any] = None
|
|
19
20
|
|
|
20
21
|
def __init__(self, db: AsyncSession):
|
|
21
22
|
"""Inicializa el repositorio con la sesión a reutilizar."""
|
|
22
23
|
self._session = db
|
|
24
|
+
self.service = None
|
|
23
25
|
|
|
24
26
|
@property
|
|
25
27
|
def session(self) -> AsyncSession:
|
|
@@ -50,6 +52,51 @@ class BaseRepository:
|
|
|
50
52
|
query = query.options(joinedload(relationship_attr))
|
|
51
53
|
return query
|
|
52
54
|
|
|
55
|
+
def _resolve_field_path(
|
|
56
|
+
self, path: str
|
|
57
|
+
) -> Tuple[Optional[Any], Dict[str, Any]]:
|
|
58
|
+
"""
|
|
59
|
+
Resuelve una ruta de campo (ej. 'user__role__name') a su atributo
|
|
60
|
+
de SQLAlchemy y el conjunto de JOINs necesarios para el filtrado.
|
|
61
|
+
|
|
62
|
+
Args:
|
|
63
|
+
path: Ruta del campo con sintaxis '__'
|
|
64
|
+
|
|
65
|
+
Returns:
|
|
66
|
+
Tuple con:
|
|
67
|
+
- attr: Atributo final (columna o relación) o None si no existe
|
|
68
|
+
- joins_to_apply: Dict con las relaciones intermedias (name: attr)
|
|
69
|
+
"""
|
|
70
|
+
parts = path.split("__")
|
|
71
|
+
current_model = self.model
|
|
72
|
+
attr = getattr(current_model, parts[0], None)
|
|
73
|
+
joins_to_apply = {}
|
|
74
|
+
|
|
75
|
+
if attr is None:
|
|
76
|
+
return None, {}
|
|
77
|
+
|
|
78
|
+
# Recorrer la cadena de relaciones intermedias
|
|
79
|
+
for part in parts[1:]:
|
|
80
|
+
# Chequear si el atributo actual es una relación ORM
|
|
81
|
+
if hasattr(attr.property, "entity") and isinstance(
|
|
82
|
+
attr.property, Relationship
|
|
83
|
+
):
|
|
84
|
+
# Es una relación: Añadir al diccionario de JOINs
|
|
85
|
+
relation_name = attr.key
|
|
86
|
+
joins_to_apply[relation_name] = attr
|
|
87
|
+
|
|
88
|
+
# Mover al modelo relacionado y obtener el siguiente atributo
|
|
89
|
+
current_model = attr.property.mapper.class_
|
|
90
|
+
attr = getattr(current_model, part, None)
|
|
91
|
+
|
|
92
|
+
if attr is None:
|
|
93
|
+
return None, {}
|
|
94
|
+
else:
|
|
95
|
+
# No es una relación o es el final del path
|
|
96
|
+
break
|
|
97
|
+
|
|
98
|
+
return attr, joins_to_apply
|
|
99
|
+
|
|
53
100
|
def _resolve_attribute(
|
|
54
101
|
self, filters: Dict[str, Any]
|
|
55
102
|
) -> Tuple[Dict[str, Tuple[Any, Any]], Dict[str, Any]]:
|
|
@@ -84,17 +131,13 @@ class BaseRepository:
|
|
|
84
131
|
joins_to_apply = {}
|
|
85
132
|
|
|
86
133
|
for filter_path, value in filters.items():
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
# Inicialización
|
|
90
|
-
current_model = self.model
|
|
91
|
-
attr = getattr(current_model, parts[0], None)
|
|
134
|
+
attr, field_joins = self._resolve_field_path(filter_path)
|
|
92
135
|
|
|
93
136
|
if attr is None:
|
|
94
|
-
# Si no existe el campo base, omitir silenciosamente
|
|
95
|
-
# (mantiene compatibilidad con código existente)
|
|
96
137
|
continue
|
|
97
138
|
|
|
139
|
+
joins_to_apply.update(field_joins)
|
|
140
|
+
|
|
98
141
|
# Procesar valor (manejo de Enum a su valor/lista de valores)
|
|
99
142
|
processed_value = value
|
|
100
143
|
if (
|
|
@@ -106,47 +149,14 @@ class BaseRepository:
|
|
|
106
149
|
elif hasattr(value, "value"):
|
|
107
150
|
processed_value = value.value
|
|
108
151
|
|
|
109
|
-
#
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
relation_name = (
|
|
118
|
-
attr.key
|
|
119
|
-
) # Nombre del atributo en el modelo
|
|
120
|
-
# (e.g., 'user_roles')
|
|
121
|
-
joins_to_apply[relation_name] = attr
|
|
122
|
-
|
|
123
|
-
# Mover al modelo relacionado y obtener el siguiente
|
|
124
|
-
# atributo
|
|
125
|
-
current_model = attr.property.mapper.class_
|
|
126
|
-
attr = getattr(current_model, part, None)
|
|
127
|
-
|
|
128
|
-
if attr is None:
|
|
129
|
-
break # El siguiente campo no existe
|
|
130
|
-
else:
|
|
131
|
-
# No es una relación (parte final del path), romper el
|
|
132
|
-
# bucle
|
|
133
|
-
break
|
|
134
|
-
|
|
135
|
-
# 2. Si el atributo final es válido, añadir a las condiciones
|
|
136
|
-
# resueltas
|
|
137
|
-
# Verificamos si es una Columna (o un campo scalar)
|
|
138
|
-
# Para columnas simples, attr.property.columns existe
|
|
139
|
-
# Para relaciones, attr.property es Relationship
|
|
140
|
-
if attr is not None:
|
|
141
|
-
# Verificar que no sea una relación (solo queremos columnas)
|
|
142
|
-
is_relationship = isinstance(attr.property, Relationship)
|
|
143
|
-
# Verificar que sea una columna o atributo válido
|
|
144
|
-
is_column = hasattr(attr.property, "columns") or hasattr(
|
|
145
|
-
attr, "comparator"
|
|
146
|
-
)
|
|
147
|
-
|
|
148
|
-
if not is_relationship and is_column:
|
|
149
|
-
resolved_filters[filter_path] = (attr, processed_value)
|
|
152
|
+
# Verificar que no sea una relación (solo queremos columnas para filtrar)
|
|
153
|
+
is_relationship = isinstance(attr.property, Relationship)
|
|
154
|
+
is_column = hasattr(attr.property, "columns") or hasattr(
|
|
155
|
+
attr, "comparator"
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
if not is_relationship and is_column:
|
|
159
|
+
resolved_filters[filter_path] = (attr, processed_value)
|
|
150
160
|
|
|
151
161
|
return resolved_filters, joins_to_apply
|
|
152
162
|
|
|
@@ -233,53 +243,21 @@ class BaseRepository:
|
|
|
233
243
|
order_expression = attr.desc() if should_descend else attr.asc()
|
|
234
244
|
return order_expression, {}
|
|
235
245
|
|
|
236
|
-
# 2. Si no es un campo especial, procesar
|
|
237
|
-
|
|
238
|
-
parts = field_path.split("__")
|
|
239
|
-
|
|
240
|
-
# Inicialización: empezar con el modelo base
|
|
241
|
-
current_model = self.model
|
|
242
|
-
attr = getattr(current_model, parts[0], None)
|
|
246
|
+
# 2. Si no es un campo especial, procesar con el helper
|
|
247
|
+
attr, field_joins = self._resolve_field_path(field_path)
|
|
243
248
|
|
|
244
249
|
if attr is None:
|
|
245
|
-
# Campo base no existe, retornar None
|
|
246
250
|
return None, {}
|
|
247
251
|
|
|
248
|
-
|
|
249
|
-
for part in parts[1:]:
|
|
250
|
-
# Chequear si el atributo actual es una relación ORM
|
|
251
|
-
if hasattr(attr.property, "entity") and isinstance(
|
|
252
|
-
attr.property, Relationship
|
|
253
|
-
):
|
|
254
|
-
# Es una relación: Añadir al diccionario de JOINs si no está ya
|
|
255
|
-
relation_name = attr.key # Nombre del atributo en el modelo
|
|
256
|
-
joins_to_apply[relation_name] = attr
|
|
257
|
-
|
|
258
|
-
# Mover al modelo relacionado y obtener el siguiente atributo
|
|
259
|
-
current_model = attr.property.mapper.class_
|
|
260
|
-
attr = getattr(current_model, part, None)
|
|
261
|
-
|
|
262
|
-
if attr is None:
|
|
263
|
-
# El siguiente campo no existe
|
|
264
|
-
return None, {}
|
|
265
|
-
else:
|
|
266
|
-
# No es una relación (parte final del path), romper el bucle
|
|
267
|
-
break
|
|
252
|
+
joins_to_apply.update(field_joins)
|
|
268
253
|
|
|
269
254
|
# 4. Verificar que el atributo final es válido para ordenar
|
|
270
|
-
# Solo queremos columnas, no relaciones
|
|
271
|
-
if attr is None:
|
|
272
|
-
return None, {}
|
|
273
|
-
|
|
274
|
-
# Verificar que no sea una relación (solo queremos columnas)
|
|
275
255
|
is_relationship = isinstance(attr.property, Relationship)
|
|
276
|
-
# Verificar que sea una columna o atributo válido
|
|
277
256
|
is_column = hasattr(attr.property, "columns") or hasattr(
|
|
278
257
|
attr, "comparator"
|
|
279
258
|
)
|
|
280
259
|
|
|
281
260
|
if is_relationship or not is_column:
|
|
282
|
-
# No es una columna válida para ordenar
|
|
283
261
|
return None, {}
|
|
284
262
|
|
|
285
263
|
# 5. Determinar la dirección del ordenamiento
|
|
@@ -337,25 +315,44 @@ class BaseRepository:
|
|
|
337
315
|
|
|
338
316
|
def _build_search_condition(
|
|
339
317
|
self, search: Optional[str], search_fields: Optional[List[str]]
|
|
340
|
-
) -> Optional[Any]:
|
|
318
|
+
) -> Tuple[Optional[Any], Dict[str, Any]]:
|
|
341
319
|
"""Construye una condición OR para búsqueda textual en múltiples campos
|
|
342
320
|
|
|
343
321
|
Usa ilike (insensible a mayúsculas) si hay término y campos válidos.
|
|
344
|
-
|
|
322
|
+
Soporta rutas anidadas con '__'.
|
|
323
|
+
|
|
324
|
+
Returns:
|
|
325
|
+
Tuple con:
|
|
326
|
+
- search_condition: Condición OR de SQLAlchemy o None
|
|
327
|
+
- search_joins: Dict con JOINs necesarios para la búsqueda
|
|
345
328
|
"""
|
|
346
329
|
if not search or not search_fields:
|
|
347
|
-
return None
|
|
330
|
+
return None, {}
|
|
331
|
+
|
|
348
332
|
exprs: List[Any] = []
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
333
|
+
search_joins: Dict[str, Any] = {}
|
|
334
|
+
|
|
335
|
+
for field_path in search_fields:
|
|
336
|
+
attr, field_joins = self._resolve_field_path(field_path)
|
|
337
|
+
|
|
338
|
+
if attr is None:
|
|
354
339
|
continue
|
|
355
|
-
|
|
340
|
+
|
|
341
|
+
search_joins.update(field_joins)
|
|
342
|
+
|
|
343
|
+
# Verificar que sea una columna válida para ILIKE
|
|
344
|
+
is_column = hasattr(attr.property, "columns") or hasattr(
|
|
345
|
+
attr, "comparator"
|
|
346
|
+
)
|
|
347
|
+
is_relationship = isinstance(attr.property, Relationship)
|
|
348
|
+
|
|
349
|
+
if not is_relationship and is_column:
|
|
350
|
+
exprs.append(attr.ilike(f"%{search}%"))
|
|
351
|
+
|
|
356
352
|
if not exprs:
|
|
357
|
-
return None
|
|
358
|
-
|
|
353
|
+
return None, {}
|
|
354
|
+
|
|
355
|
+
return or_(*exprs), search_joins
|
|
359
356
|
|
|
360
357
|
async def create(self, obj_in: Any | Dict) -> Any:
|
|
361
358
|
"""Crea un nuevo registro en la base de datos."""
|
|
@@ -578,9 +575,17 @@ class BaseRepository:
|
|
|
578
575
|
)
|
|
579
576
|
|
|
580
577
|
# 4. Aplicar búsqueda textual (search)
|
|
581
|
-
search_condition = self._build_search_condition(
|
|
578
|
+
search_condition, search_joins = self._build_search_condition(
|
|
579
|
+
search, search_fields
|
|
580
|
+
)
|
|
581
|
+
|
|
582
|
+
# 5. Aplicar JOINs de búsqueda
|
|
583
|
+
for relation_name, relation_attr in search_joins.items():
|
|
584
|
+
# Evitar unir dos veces la misma relación si ya se hizo por filtros
|
|
585
|
+
# NOTA: join() en SQLAlchemy es idempotente si es la misma entidad/atributo
|
|
586
|
+
queryset = queryset.join(relation_attr)
|
|
582
587
|
|
|
583
|
-
#
|
|
588
|
+
# 6. Combina todos los filtros
|
|
584
589
|
if combined_filters is not None and search_condition is not None:
|
|
585
590
|
queryset = queryset.where(
|
|
586
591
|
and_(combined_filters, search_condition)
|
|
@@ -625,12 +630,11 @@ class BaseRepository:
|
|
|
625
630
|
order_by: Optional[Any] = None,
|
|
626
631
|
search: Optional[str] = None,
|
|
627
632
|
search_fields: Optional[List[str]] = None,
|
|
628
|
-
base_query: Optional[Select[Tuple[Any]]] = None,
|
|
629
633
|
**kwargs: Any,
|
|
630
634
|
) -> tuple[List[Any], int]:
|
|
631
635
|
|
|
632
|
-
# 1. Construir query
|
|
633
|
-
|
|
636
|
+
# 1. Construir query base
|
|
637
|
+
query_kwargs = {
|
|
634
638
|
"filters": filters,
|
|
635
639
|
"use_or": use_or,
|
|
636
640
|
"joins": joins,
|
|
@@ -638,13 +642,12 @@ class BaseRepository:
|
|
|
638
642
|
"search": search,
|
|
639
643
|
"search_fields": search_fields,
|
|
640
644
|
}
|
|
641
|
-
|
|
642
645
|
try:
|
|
643
|
-
|
|
644
|
-
|
|
646
|
+
# Intentar con argumentos (subclases modernas)
|
|
647
|
+
queryset = self.build_list_queryset(**query_kwargs)
|
|
645
648
|
except TypeError:
|
|
646
|
-
|
|
647
|
-
|
|
649
|
+
# Fallback sin argumentos (subclases legacy)
|
|
650
|
+
queryset = self.build_list_queryset()
|
|
648
651
|
|
|
649
652
|
# 2. Aplicar filtros estándar
|
|
650
653
|
queryset = self.apply_list_filters(
|
{fastapi_basekit-0.1.24 → fastapi_basekit-0.1.25}/fastapi_basekit/aio/sqlalchemy/service/base.py
RENAMED
|
@@ -23,14 +23,37 @@ class BaseService:
|
|
|
23
23
|
self,
|
|
24
24
|
repository: BaseRepository,
|
|
25
25
|
request: Optional[Request] = None,
|
|
26
|
+
**kwargs,
|
|
26
27
|
):
|
|
27
28
|
self.repository = repository
|
|
28
29
|
self.request = request
|
|
30
|
+
|
|
31
|
+
# Vincular el servicio al repositorio principal
|
|
32
|
+
if self.repository:
|
|
33
|
+
self.repository.service = self
|
|
34
|
+
|
|
35
|
+
# Procesar kwargs adicionales para vincular otros repositorios
|
|
36
|
+
for name, value in kwargs.items():
|
|
37
|
+
if isinstance(value, BaseRepository):
|
|
38
|
+
value.service = self
|
|
39
|
+
setattr(self, name, value)
|
|
29
40
|
endpoint_func = (
|
|
30
41
|
self.request.scope.get("endpoint") if self.request else None
|
|
31
42
|
)
|
|
32
43
|
self.action = endpoint_func.__name__ if endpoint_func else None
|
|
33
44
|
|
|
45
|
+
# Parámetros compartidos para consultas (especialmente list)
|
|
46
|
+
self.params: Dict[str, Any] = {
|
|
47
|
+
"search": None,
|
|
48
|
+
"page": 1,
|
|
49
|
+
"count": 25,
|
|
50
|
+
"filters": {},
|
|
51
|
+
"use_or": False,
|
|
52
|
+
"joins": None,
|
|
53
|
+
"order_by": None,
|
|
54
|
+
"meta": {}
|
|
55
|
+
}
|
|
56
|
+
|
|
34
57
|
def get_filters(
|
|
35
58
|
self, filters: Optional[Dict[str, Any]] = None
|
|
36
59
|
) -> Dict[str, Any]:
|
|
@@ -51,27 +74,6 @@ class BaseService:
|
|
|
51
74
|
"""
|
|
52
75
|
return self.kwargs_query or {}
|
|
53
76
|
|
|
54
|
-
def build_queryset(self) -> Select[Tuple[Any]]:
|
|
55
|
-
"""Construye el queryset base para las consultas de listado.
|
|
56
|
-
|
|
57
|
-
Este método puede ser sobrescrito por el usuario para personalizar
|
|
58
|
-
el queryset base (agregar joins, agregaciones, etc.) sin necesidad
|
|
59
|
-
de reescribir el método list().
|
|
60
|
-
|
|
61
|
-
Ejemplo de uso:
|
|
62
|
-
def build_queryset(self):
|
|
63
|
-
from sqlalchemy import func, select
|
|
64
|
-
from .models import User, Referral
|
|
65
|
-
|
|
66
|
-
return select(
|
|
67
|
-
User,
|
|
68
|
-
func.count(Referral.id).label('referidos_count')
|
|
69
|
-
).outerjoin(Referral, User.id == Referral.user_id).group_by(User.id)
|
|
70
|
-
|
|
71
|
-
Returns:
|
|
72
|
-
Select: Query base de SQLAlchemy
|
|
73
|
-
"""
|
|
74
|
-
return select(self.repository.model)
|
|
75
77
|
|
|
76
78
|
async def retrieve(
|
|
77
79
|
self, id: str, joins: Optional[List[str]] = None
|
|
@@ -91,33 +93,52 @@ class BaseService:
|
|
|
91
93
|
async def list(
|
|
92
94
|
self,
|
|
93
95
|
search: Optional[str] = None,
|
|
94
|
-
page: int =
|
|
95
|
-
count: int =
|
|
96
|
+
page: Optional[int] = None,
|
|
97
|
+
count: Optional[int] = None,
|
|
96
98
|
filters: Optional[Dict[str, Any]] = None,
|
|
97
|
-
use_or: bool =
|
|
99
|
+
use_or: Optional[bool] = None,
|
|
98
100
|
joins: Optional[List[str]] = None,
|
|
99
101
|
order_by: Optional[Any] = None,
|
|
100
102
|
) -> tuple[List[Any], int]:
|
|
101
|
-
#
|
|
102
|
-
|
|
103
|
+
# Actualiza self.params con los argumentos proporcionados (si no son None)
|
|
104
|
+
if search is not None:
|
|
105
|
+
self.params["search"] = search
|
|
106
|
+
if page is not None:
|
|
107
|
+
self.params["page"] = page
|
|
108
|
+
if count is not None:
|
|
109
|
+
self.params["count"] = count
|
|
110
|
+
if filters is not None:
|
|
111
|
+
self.params["filters"] = filters
|
|
112
|
+
if use_or is not None:
|
|
113
|
+
self.params["use_or"] = use_or
|
|
114
|
+
if joins is not None:
|
|
115
|
+
self.params["joins"] = joins
|
|
116
|
+
if order_by is not None:
|
|
117
|
+
self.params["order_by"] = order_by
|
|
118
|
+
|
|
103
119
|
|
|
104
120
|
# Aplica filtros y kwargs de consulta definidos por el servicio
|
|
105
|
-
|
|
121
|
+
applied_filters = self.get_filters(self.params["filters"])
|
|
106
122
|
kwargs = self.get_kwargs_query()
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
123
|
+
|
|
124
|
+
# Prioridad de joins: argumento explícito > kwargs del servicio (por acción)
|
|
125
|
+
final_joins = self.params["joins"]
|
|
126
|
+
if final_joins is None:
|
|
127
|
+
final_joins = kwargs.get("joins")
|
|
128
|
+
|
|
129
|
+
# Prioridad de order_by: argumento explícito > kwargs del servicio > default del servicio
|
|
130
|
+
final_order_by = self.params["order_by"]
|
|
131
|
+
if final_order_by is None:
|
|
132
|
+
final_order_by = kwargs.get("order_by", self.order_by)
|
|
111
133
|
|
|
112
134
|
return await self.repository.list_paginated(
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
search=search,
|
|
135
|
+
page=self.params["page"],
|
|
136
|
+
count=self.params["count"],
|
|
137
|
+
filters=applied_filters,
|
|
138
|
+
use_or=self.params["use_or"],
|
|
139
|
+
joins=final_joins,
|
|
140
|
+
order_by=final_order_by,
|
|
141
|
+
search=self.params["search"],
|
|
121
142
|
search_fields=self.search_fields,
|
|
122
143
|
)
|
|
123
144
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "fastapi-basekit"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.25"
|
|
8
8
|
description = "Utilities and base classes for FastAPI async projects (Beanie or SQLAlchemy)"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fastapi_basekit-0.1.24 → fastapi_basekit-0.1.25}/fastapi_basekit/aio/beanie/controller/__init__.py
RENAMED
|
File without changes
|
{fastapi_basekit-0.1.24 → fastapi_basekit-0.1.25}/fastapi_basekit/aio/beanie/controller/base.py
RENAMED
|
File without changes
|
{fastapi_basekit-0.1.24 → fastapi_basekit-0.1.25}/fastapi_basekit/aio/beanie/repository/__init__.py
RENAMED
|
File without changes
|
{fastapi_basekit-0.1.24 → fastapi_basekit-0.1.25}/fastapi_basekit/aio/beanie/repository/base.py
RENAMED
|
File without changes
|
{fastapi_basekit-0.1.24 → fastapi_basekit-0.1.25}/fastapi_basekit/aio/beanie/service/__init__.py
RENAMED
|
File without changes
|
{fastapi_basekit-0.1.24 → fastapi_basekit-0.1.25}/fastapi_basekit/aio/beanie/service/base.py
RENAMED
|
File without changes
|
{fastapi_basekit-0.1.24 → fastapi_basekit-0.1.25}/fastapi_basekit/aio/controller/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{fastapi_basekit-0.1.24 → fastapi_basekit-0.1.25}/fastapi_basekit/aio/permissions/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{fastapi_basekit-0.1.24 → fastapi_basekit-0.1.25}/fastapi_basekit/aio/sqlalchemy/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fastapi_basekit-0.1.24 → fastapi_basekit-0.1.25}/fastapi_basekit/aio/sqlalchemy/service/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{fastapi_basekit-0.1.24 → fastapi_basekit-0.1.25}/fastapi_basekit/exceptions/api_exceptions.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fastapi_basekit-0.1.24 → fastapi_basekit-0.1.25}/fastapi_basekit/servicios/thrid/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fastapi_basekit-0.1.24 → fastapi_basekit-0.1.25}/fastapi_basekit.egg-info/dependency_links.txt
RENAMED
|
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
|