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