add: BaseApiViewMixin created, TODO: switch external endpoints to GenericAPIView and BaseApiViewMixin
This commit is contained in:
93
core/utils/views.py
Normal file
93
core/utils/views.py
Normal file
@@ -0,0 +1,93 @@
|
||||
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
|
||||
Reference in New Issue
Block a user