Files
trustme/core/utils/views.py

94 lines
3.4 KiB
Python

from typing import Sequence, cast, Type, override
from rest_framework.request import HttpRequest # type: ignore
from rest_framework.response import Response # type: ignore
from rest_framework.serializers import Serializer # type: ignore
from rest_framework.permissions import BasePermission # type: ignore
class BaseApiViewMixin:
"""
A reusable mixin for DRF GenericAPIView-based views, inspired by `BaseViewSetMixin`.
This mixin provides method-specific serializer and permission class resolution,
and overrides `finalize_response()` to return a standardized response structure.
"""
serializer_class: Type[Serializer]
permission_classes: Sequence[Type[BasePermission]]
method_serializer_class: dict[str, Type[Serializer]] = {}
method_permission_classes: dict[str, Sequence[Type[BasePermission]]] = {}
@override
def finalize_response( # type: ignore
self,
request: HttpRequest,
response: Response,
*args: object,
**kwargs: object
) -> Response:
"""
Finalizes the response by wrapping it in a standardized format.
- If the status code is 2xx, the response is wrapped as: {"ok": True, "data": ...}
- If the status code is >= 400, the response becomes: {"ok": False, "data": ...}
- If the status code is 204 (No Content), the response is left untouched.
This behavior is designed to mimic the uniform API structure used in `BaseViewSetMixin`.
Returns:
Response: A DRF Response object with standardized content structure.
"""
if response.status_code >= 400:
response.data = {"ok": False, "data": response.data} # type: ignore
elif response.status_code == 204:
pass
else:
response.data = {"ok": True, "data": response.data} # type: ignore
return super().finalize_response(request, response, *args, **kwargs) # type: ignore
@override
def get_serializer_class(self) -> Type[Serializer]: # type: ignore
"""
Returns the serializer class based on the current request method.
Falls back to the default `serializer_class` if no method-specific
serializer is provided in `method_serializer_class`.
Returns:
Type[Serializer]: The resolved serializer class.
"""
return self.method_serializer_class.get(
self.__get_request_method(),
self.serializer_class
)
@override
def get_permissions(self) -> list[BasePermission]: # type: ignore
"""
Returns a list of permission instances based on the current request method.
Falls back to the default `permission_classes` if no method-specific
permissions are defined in `method_permission_classes`.
Returns:
list[BasePermission]: A list of permission instances.
"""
return [
permission()
for permission in self.method_permission_classes.get(
self.__get_request_method(), self.permission_classes
)
]
def __get_request_method(self) -> str:
"""
Returns the HTTP method of the current request in lowercase form.
Used internally to resolve method-specific serializers and permissions.
Returns:
str: The request method (e.g., 'get', 'post', 'put', etc.).
"""
return cast(str, self.request.method).lower() # type: ignore # noqa