fastapi-basekit 0.3.2__tar.gz → 0.3.3__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.3.2 → fastapi_basekit-0.3.3}/PKG-INFO +1 -1
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/aio/beanie/controller/base.py +2 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/aio/controller/base.py +111 -41
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/aio/sqlalchemy/controller/base.py +3 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/aio/sqlmodel/controller/base.py +3 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit.egg-info/PKG-INFO +1 -1
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit.egg-info/SOURCES.txt +4 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/pyproject.toml +1 -1
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/tests/test_base_service.py +18 -1
- fastapi_basekit-0.3.3/tests/test_beanie_aggregation_hooks.py +484 -0
- fastapi_basekit-0.3.3/tests/test_beanie_aggregation_integration.py +199 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/tests/test_controller_auto_permissions.py +56 -71
- fastapi_basekit-0.3.3/tests/test_crud_sqlmodel_repository_service.py +242 -0
- fastapi_basekit-0.3.3/tests/test_sql_queryset_override.py +332 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/LICENSE +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/README.md +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/__init__.py +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/aio/__init__.py +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/aio/beanie/__init__.py +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/aio/beanie/controller/__init__.py +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/aio/beanie/repository/__init__.py +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/aio/beanie/repository/base.py +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/aio/beanie/service/__init__.py +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/aio/beanie/service/base.py +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/aio/controller/__init__.py +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/aio/permissions/__init__.py +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/aio/permissions/base.py +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/aio/sqlalchemy/__init__.py +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/aio/sqlalchemy/controller/__init__.py +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/aio/sqlalchemy/repository/__init__.py +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/aio/sqlalchemy/repository/base.py +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/aio/sqlalchemy/service/__init__.py +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/aio/sqlalchemy/service/base.py +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/aio/sqlalchemy/session.py +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/aio/sqlmodel/__init__.py +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/aio/sqlmodel/controller/__init__.py +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/aio/sqlmodel/repository/__init__.py +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/aio/sqlmodel/repository/base.py +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/aio/sqlmodel/service/__init__.py +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/aio/sqlmodel/service/base.py +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/cli/__init__.py +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/cli/main.py +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/exceptions/__init__.py +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/exceptions/api_exceptions.py +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/exceptions/domain.py +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/exceptions/handler.py +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/schema/__init__.py +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/schema/base.py +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/schema/jwt.py +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/schema/schema.py +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/servicios/__init__.py +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/servicios/thrid/__init__.py +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/servicios/thrid/jwt.py +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/templates/project/cookiecutter.json +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/templates/project/hooks/post_gen_project.py +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/templates/project/hooks/pre_gen_project.py +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/.env.example +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/.gitignore +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/Dockerfile +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/LICENSE +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/Makefile +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/README.md +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/alembic/env.py +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/alembic/script.py.mako +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/alembic.ini +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/__init__.py +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/api/__init__.py +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/api/v1/__init__.py +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/api/v1/endpoints/__init__.py +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/api/v1/endpoints/auth/__init__.py +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/api/v1/endpoints/auth/auth.py +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/api/v1/endpoints/user/__init__.py +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/api/v1/endpoints/user/user.py +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/api/v1/routers.py +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/config/__init__.py +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/config/database.py +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/config/settings.py +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/main.py +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/middleware/__init__.py +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/middleware/auth.py +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/middleware/permissions.py +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/models/__init__.py +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/models/auth.py +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/models/base.py +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/models/enums.py +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/models/types.py +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/permissions/__init__.py +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/permissions/user.py +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/repositories/__init__.py +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/repositories/user/__init__.py +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/repositories/user/repository.py +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/schemas/__init__.py +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/schemas/auth.py +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/schemas/base.py +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/schemas/user.py +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/scripts/__init__.py +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/scripts/init.py +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/scripts/init_admin.py +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/services/__init__.py +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/services/auth_service.py +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/services/dependency.py +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/services/user_service.py +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/utils/__init__.py +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/utils/exception_handlers.py +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/utils/security.py +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/docker-compose.yml +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/pytest.ini +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/requirements.txt +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit.egg-info/dependency_links.txt +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit.egg-info/entry_points.txt +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit.egg-info/requires.txt +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit.egg-info/top_level.txt +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/setup.cfg +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/tests/test_api_exceptions.py +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/tests/test_base_response.py +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/tests/test_crud_beanie_controller.py +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/tests/test_crud_controller.py +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/tests/test_jwt_service.py +0 -0
- {fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/tests/test_sqlalchemy_base_service_order.py +0 -0
{fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/aio/beanie/controller/base.py
RENAMED
|
@@ -18,6 +18,7 @@ class BeanieBaseController(BaseController):
|
|
|
18
18
|
|
|
19
19
|
async def list(self):
|
|
20
20
|
"""Lista documentos con paginación usando Beanie."""
|
|
21
|
+
await self.prepare_action("list")
|
|
21
22
|
params = self._params(skip_frames=2)
|
|
22
23
|
items, total = await self.service.list(**params)
|
|
23
24
|
count = params.get("count") or 0
|
|
@@ -39,6 +40,7 @@ class BeanieBaseController(BaseController):
|
|
|
39
40
|
check_fields: Optional[List[str]] = None,
|
|
40
41
|
):
|
|
41
42
|
"""Crea un nuevo documento con validación de campos únicos."""
|
|
43
|
+
await self.prepare_action("create")
|
|
42
44
|
result = await self.service.create(validated_data, check_fields)
|
|
43
45
|
return self.format_response(result, message="Creado exitosamente")
|
|
44
46
|
|
|
@@ -14,7 +14,11 @@ class BaseController:
|
|
|
14
14
|
|
|
15
15
|
service = Depends()
|
|
16
16
|
schema_class: ClassVar[Type[BaseModel]]
|
|
17
|
-
|
|
17
|
+
|
|
18
|
+
# DRF Style: Permisos globales por defecto
|
|
19
|
+
permission_classes: ClassVar[List[Type[BasePermission]]] = []
|
|
20
|
+
|
|
21
|
+
action: Optional[str] = None
|
|
18
22
|
request: Request
|
|
19
23
|
_params_excluded_fields: ClassVar[Set[str]] = {
|
|
20
24
|
"self",
|
|
@@ -32,34 +36,73 @@ class BaseController:
|
|
|
32
36
|
}
|
|
33
37
|
|
|
34
38
|
def __init__(self) -> None:
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
"""Inicializa el controller."""
|
|
40
|
+
pass
|
|
41
|
+
|
|
42
|
+
def get_permissions(self) -> List[Type[BasePermission]]:
|
|
43
|
+
"""
|
|
44
|
+
Instancia y retorna la lista de permisos que esta vista requiere.
|
|
45
|
+
|
|
46
|
+
Sobrescribir esto permite lógica tipo DRF:
|
|
47
|
+
|
|
48
|
+
if self.action == 'list':
|
|
49
|
+
return [AllowAny]
|
|
50
|
+
return [IsAuthenticated]
|
|
51
|
+
"""
|
|
52
|
+
return self.permission_classes
|
|
53
|
+
|
|
54
|
+
async def prepare_action(self, action_name: str) -> None:
|
|
55
|
+
"""Set the current action and run permission checks.
|
|
56
|
+
|
|
57
|
+
Auto-called by ``ControllerMeta`` for every public async method on
|
|
58
|
+
the controller. Idempotent within one invocation: if the same
|
|
59
|
+
``action_name`` has already been prepared on this instance,
|
|
60
|
+
subsequent calls are no-ops. This lets custom methods opt into
|
|
61
|
+
calling ``await self.prepare_action(...)`` explicitly without
|
|
62
|
+
double-firing permission checks (when the metaclass already ran
|
|
63
|
+
before entering the method body).
|
|
64
|
+
"""
|
|
65
|
+
if getattr(self, "_basekit_prepared_action", None) == action_name:
|
|
66
|
+
return
|
|
67
|
+
self.action = action_name
|
|
68
|
+
self._basekit_prepared_action = action_name
|
|
69
|
+
await self.check_permissions()
|
|
70
|
+
|
|
71
|
+
async def check_permissions(self):
|
|
72
|
+
"""Run each declared permission. Raises ``PermissionException``
|
|
73
|
+
on the first denial.
|
|
74
|
+
"""
|
|
75
|
+
for permission_class in self.get_permissions():
|
|
76
|
+
permission = permission_class()
|
|
77
|
+
has_perm = await permission.has_permission(self.request)
|
|
78
|
+
if not has_perm:
|
|
79
|
+
message = getattr(
|
|
80
|
+
permission,
|
|
81
|
+
"message_exception",
|
|
82
|
+
"No tienes permiso para realizar esta acción.",
|
|
83
|
+
)
|
|
84
|
+
raise PermissionException(message)
|
|
85
|
+
|
|
86
|
+
async def check_permissions_class(self):
|
|
87
|
+
"""Backward-compat alias for ``check_permissions``.
|
|
88
|
+
|
|
89
|
+
Pre-0.3.2 controllers called this manually inside endpoint methods.
|
|
90
|
+
Keep working for users who haven't migrated to ``permission_classes``
|
|
91
|
+
+ auto-wrapping yet. New code should declare ``permission_classes``
|
|
92
|
+
on the controller and let the metaclass run permissions.
|
|
93
|
+
"""
|
|
94
|
+
await self.check_permissions()
|
|
41
95
|
|
|
42
96
|
def get_schema_class(self) -> Type[BaseModel]:
|
|
43
97
|
assert self.schema_class is not None, (
|
|
44
98
|
"'%s' should either include a `schema_class` attribute, "
|
|
45
|
-
"or override the `
|
|
99
|
+
"or override the `get_schema_class()` method."
|
|
46
100
|
% self.__class__.__name__
|
|
47
101
|
)
|
|
48
102
|
return self.schema_class
|
|
49
103
|
|
|
50
|
-
async def check_permissions_class(self):
|
|
51
|
-
permissions = self.check_permissions()
|
|
52
|
-
if permissions:
|
|
53
|
-
for permission in permissions:
|
|
54
|
-
obj = permission()
|
|
55
|
-
check = await obj.has_permission(self.request)
|
|
56
|
-
if not check:
|
|
57
|
-
raise PermissionException(obj.message_exception)
|
|
58
|
-
|
|
59
|
-
def check_permissions(self) -> List[Type[BasePermission]]:
|
|
60
|
-
pass
|
|
61
|
-
|
|
62
104
|
async def list(self):
|
|
105
|
+
await self.prepare_action("list")
|
|
63
106
|
params = self._params()
|
|
64
107
|
items, total = await self.service.list(**params)
|
|
65
108
|
count = params.get("count") or 0
|
|
@@ -75,18 +118,22 @@ class BaseController:
|
|
|
75
118
|
return self.format_response(data=items, pagination=pagination)
|
|
76
119
|
|
|
77
120
|
async def retrieve(self, id: str):
|
|
121
|
+
await self.prepare_action("retrieve")
|
|
78
122
|
item = await self.service.retrieve(id)
|
|
79
123
|
return self.format_response(data=item)
|
|
80
124
|
|
|
81
125
|
async def create(self, validated_data: Any):
|
|
126
|
+
await self.prepare_action("create")
|
|
82
127
|
result = await self.service.create(validated_data)
|
|
83
128
|
return self.format_response(result, message="Creado exitosamente")
|
|
84
129
|
|
|
85
130
|
async def update(self, id: str, validated_data: Any):
|
|
131
|
+
await self.prepare_action("update")
|
|
86
132
|
result = await self.service.update(id, validated_data)
|
|
87
133
|
return self.format_response(result, message="Actualizado exitosamente")
|
|
88
134
|
|
|
89
135
|
async def delete(self, id: str):
|
|
136
|
+
await self.prepare_action("delete")
|
|
90
137
|
await self.service.delete(id)
|
|
91
138
|
return self.format_response(None, message="Eliminado exitosamente")
|
|
92
139
|
|
|
@@ -99,33 +146,48 @@ class BaseController:
|
|
|
99
146
|
) -> BaseModel:
|
|
100
147
|
schema = self.get_schema_class()
|
|
101
148
|
|
|
149
|
+
# Robust Pydantic v2 validation. Each branch falls back to the
|
|
150
|
+
# raw value when the schema doesn't fit (custom-action endpoints
|
|
151
|
+
# often return ad-hoc dicts that don't match the controller's
|
|
152
|
+
# default schema_class — those should pass through untouched).
|
|
102
153
|
if isinstance(data, list):
|
|
103
154
|
data_dicts = [self.to_dict(item) for item in data]
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
data_parsed = schema.model_validate(data_parsed)
|
|
155
|
+
try:
|
|
156
|
+
adapter = TypeAdapter(List[schema])
|
|
157
|
+
data_parsed = adapter.validate_python(data_dicts)
|
|
158
|
+
except Exception:
|
|
159
|
+
data_parsed = data_dicts
|
|
160
|
+
|
|
111
161
|
elif isinstance(data, dict):
|
|
112
|
-
|
|
162
|
+
try:
|
|
163
|
+
data_parsed = schema.model_validate(data)
|
|
164
|
+
except Exception:
|
|
165
|
+
data_parsed = data
|
|
166
|
+
|
|
167
|
+
elif hasattr(data, "__dict__"):
|
|
168
|
+
data_dict = self.to_dict(data)
|
|
169
|
+
try:
|
|
170
|
+
data_parsed = schema.model_validate(data_dict)
|
|
171
|
+
except Exception:
|
|
172
|
+
data_parsed = data_dict
|
|
173
|
+
|
|
174
|
+
elif data is None:
|
|
175
|
+
data_parsed = None
|
|
113
176
|
else:
|
|
114
177
|
data_parsed = data
|
|
115
178
|
|
|
179
|
+
response_cls = BasePaginationResponse if pagination else BaseResponse
|
|
180
|
+
|
|
181
|
+
# Construcción dinámica de argumentos
|
|
182
|
+
kwargs = {
|
|
183
|
+
"data": data_parsed,
|
|
184
|
+
"message": message or "Operación exitosa",
|
|
185
|
+
"status": response_status,
|
|
186
|
+
}
|
|
116
187
|
if pagination:
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
message=message or "Operación exitosa",
|
|
121
|
-
status=response_status,
|
|
122
|
-
)
|
|
123
|
-
else:
|
|
124
|
-
return BaseResponse(
|
|
125
|
-
data=data_parsed,
|
|
126
|
-
message=message or "Operación exitosa",
|
|
127
|
-
status=response_status,
|
|
128
|
-
)
|
|
188
|
+
kwargs["pagination"] = pagination
|
|
189
|
+
|
|
190
|
+
return response_cls(**kwargs)
|
|
129
191
|
|
|
130
192
|
def _params(self, skip_frames: int = 1) -> Dict[str, Any]:
|
|
131
193
|
"""
|
|
@@ -208,6 +270,14 @@ class BaseController:
|
|
|
208
270
|
}
|
|
209
271
|
|
|
210
272
|
def to_dict(self, obj: Any):
|
|
211
|
-
|
|
273
|
+
"""Helper para convertir modelos ORM/Pydantic a dict."""
|
|
274
|
+
if hasattr(obj, "model_dump"): # Pydantic v2
|
|
212
275
|
return obj.model_dump()
|
|
276
|
+
if hasattr(obj, "dict"): # Pydantic v1
|
|
277
|
+
return obj.dict()
|
|
278
|
+
if hasattr(obj, "__dict__"): # SQLAlchemy models (basic)
|
|
279
|
+
# Filtramos atributos privados de SQLAlchemy
|
|
280
|
+
return {
|
|
281
|
+
k: v for k, v in obj.__dict__.items() if not k.startswith("_")
|
|
282
|
+
}
|
|
213
283
|
return obj
|
{fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/aio/sqlalchemy/controller/base.py
RENAMED
|
@@ -48,6 +48,7 @@ class SQLAlchemyBaseController(BaseController):
|
|
|
48
48
|
joins: Lista de relaciones a hacer JOIN eager loading
|
|
49
49
|
order_by: Expresión de ordenamiento (ej: User.created_at.desc())
|
|
50
50
|
"""
|
|
51
|
+
await self.prepare_action("list")
|
|
51
52
|
params = self._params(skip_frames=2)
|
|
52
53
|
service_params = {
|
|
53
54
|
**params,
|
|
@@ -73,6 +74,7 @@ class SQLAlchemyBaseController(BaseController):
|
|
|
73
74
|
id: ID del registro
|
|
74
75
|
joins: Lista de relaciones a hacer JOIN eager loading
|
|
75
76
|
"""
|
|
77
|
+
await self.prepare_action("retrieve")
|
|
76
78
|
item = await self.service.retrieve(id, joins=joins)
|
|
77
79
|
return self.format_response(data=item)
|
|
78
80
|
|
|
@@ -89,6 +91,7 @@ class SQLAlchemyBaseController(BaseController):
|
|
|
89
91
|
validated_data: Datos validados para crear
|
|
90
92
|
check_fields: Campos a verificar por duplicados antes de crear
|
|
91
93
|
"""
|
|
94
|
+
await self.prepare_action("create")
|
|
92
95
|
result = await self.service.create(validated_data, check_fields)
|
|
93
96
|
return self.format_response(result, message="Creado exitosamente")
|
|
94
97
|
|
{fastapi_basekit-0.3.2 → fastapi_basekit-0.3.3}/fastapi_basekit/aio/sqlmodel/controller/base.py
RENAMED
|
@@ -49,6 +49,7 @@ class SQLModelBaseController(BaseController):
|
|
|
49
49
|
joins: Lista de relaciones para eager loading.
|
|
50
50
|
order_by: Expresión de ordenamiento (ej: ``"-created_at"``).
|
|
51
51
|
"""
|
|
52
|
+
await self.prepare_action("list")
|
|
52
53
|
params = self._params(skip_frames=2)
|
|
53
54
|
service_params = {
|
|
54
55
|
**params,
|
|
@@ -73,6 +74,7 @@ class SQLModelBaseController(BaseController):
|
|
|
73
74
|
id: ID del registro.
|
|
74
75
|
joins: Lista de relaciones para eager loading.
|
|
75
76
|
"""
|
|
77
|
+
await self.prepare_action("retrieve")
|
|
76
78
|
item = await self.service.retrieve(id, joins=joins)
|
|
77
79
|
return self.format_response(data=item)
|
|
78
80
|
|
|
@@ -88,6 +90,7 @@ class SQLModelBaseController(BaseController):
|
|
|
88
90
|
validated_data: Datos validados para crear.
|
|
89
91
|
check_fields: Campos a verificar por duplicados antes de crear.
|
|
90
92
|
"""
|
|
93
|
+
await self.prepare_action("create")
|
|
91
94
|
result = await self.service.create(validated_data, check_fields)
|
|
92
95
|
return self.format_response(result, message="Creado exitosamente")
|
|
93
96
|
|
|
@@ -106,8 +106,12 @@ fastapi_basekit/templates/project/{{cookiecutter.project_slug}}/app/utils/securi
|
|
|
106
106
|
tests/test_api_exceptions.py
|
|
107
107
|
tests/test_base_response.py
|
|
108
108
|
tests/test_base_service.py
|
|
109
|
+
tests/test_beanie_aggregation_hooks.py
|
|
110
|
+
tests/test_beanie_aggregation_integration.py
|
|
109
111
|
tests/test_controller_auto_permissions.py
|
|
110
112
|
tests/test_crud_beanie_controller.py
|
|
111
113
|
tests/test_crud_controller.py
|
|
114
|
+
tests/test_crud_sqlmodel_repository_service.py
|
|
112
115
|
tests/test_jwt_service.py
|
|
116
|
+
tests/test_sql_queryset_override.py
|
|
113
117
|
tests/test_sqlalchemy_base_service_order.py
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "fastapi-basekit"
|
|
7
|
-
version = "0.3.
|
|
7
|
+
version = "0.3.3"
|
|
8
8
|
description = "Utilities and base classes for FastAPI async projects (Beanie, SQLAlchemy or SQLModel)"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11"
|
|
@@ -47,7 +47,24 @@ class FakeRepository:
|
|
|
47
47
|
if all(obj.get(k) == v for k, v in (filters or {}).items())
|
|
48
48
|
]
|
|
49
49
|
|
|
50
|
-
|
|
50
|
+
def build_list_queryset(
|
|
51
|
+
self,
|
|
52
|
+
search=None,
|
|
53
|
+
search_fields=None,
|
|
54
|
+
filters=None,
|
|
55
|
+
order_by=None,
|
|
56
|
+
**kwargs,
|
|
57
|
+
):
|
|
58
|
+
# Synchronous wrapper compatible with the new BaseService.list contract.
|
|
59
|
+
return self.build_filter_query(
|
|
60
|
+
search=search,
|
|
61
|
+
search_fields=search_fields or [],
|
|
62
|
+
filters=filters or {},
|
|
63
|
+
order_by=order_by,
|
|
64
|
+
**kwargs,
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
async def paginate(self, query, page, count, order_by=None):
|
|
51
68
|
# Si query es una coroutine, esperarla primero
|
|
52
69
|
if hasattr(query, "__await__"):
|
|
53
70
|
query = await query
|