94 lines
3.4 KiB
Python
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
|