Добавлен класс CoreViewSet и тесты к нему
This commit is contained in:
16
net/xeaf/rack/core/views/__init__.py
Normal file
16
net/xeaf/rack/core/views/__init__.py
Normal file
@@ -0,0 +1,16 @@
|
||||
# DRF Rack
|
||||
# Библиотека классов расширений для Django REST Framework
|
||||
#
|
||||
# Автор: Николай В. Анохин <n.anokhin@xeaf.net>
|
||||
# Все права защищены. Лицензия: MIT
|
||||
|
||||
"""
|
||||
Пакет базовых классов представлений
|
||||
"""
|
||||
|
||||
from .core_view_set import CoreViewSet
|
||||
from .view_set_create_mixin import ViewSetCreateMixin
|
||||
from .view_set_destroy_mixin import ViewSetDestroyMixin
|
||||
from .view_set_list_mixin import ViewSetListMixin
|
||||
from .view_set_retrieve_mixin import ViewSetRetrieveMixin
|
||||
from .view_set_update_mixin import ViewSetUpdateMixin
|
||||
283
net/xeaf/rack/core/views/core_view_set.py
Normal file
283
net/xeaf/rack/core/views/core_view_set.py
Normal file
@@ -0,0 +1,283 @@
|
||||
# DRF Rack
|
||||
# Библиотека классов расширений для Django REST Framework
|
||||
#
|
||||
# Автор: Николай В. Анохин <n.anokhin@xeaf.net>
|
||||
# Все права защищены. Лицензия: MIT
|
||||
|
||||
"""
|
||||
Описание класса CoreViewSet
|
||||
"""
|
||||
|
||||
from typing import Any
|
||||
from typing import Type
|
||||
from uuid import UUID
|
||||
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.urls import path
|
||||
from django.urls import URLPattern
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
from rest_framework.serializers import Serializer
|
||||
from rest_framework.viewsets import ViewSet
|
||||
|
||||
from net.xeaf.rack.enums import HttpMethod
|
||||
from net.xeaf.rack.enums import ViewSetAction
|
||||
from net.xeaf.rack.models import AccountModel
|
||||
from net.xeaf.rack.models import SessionModel
|
||||
from net.xeaf.rack.utils.exceptions import BadRequestException
|
||||
from net.xeaf.rack.utils.exceptions import UnauthorizedException
|
||||
|
||||
|
||||
class CoreViewSet(ViewSet):
|
||||
"""
|
||||
Реализует базовый функционал для наборов представлений API
|
||||
"""
|
||||
|
||||
permission_classes = (IsAuthenticated,)
|
||||
""" Классы проверки прав доступа """
|
||||
|
||||
item_list_pattern: str | None = None
|
||||
""" Путь для работы со списком объектов """
|
||||
|
||||
item_detail_pattern: str | None = None
|
||||
""" Путь для работы с объектом """
|
||||
|
||||
response_serializer: Type[Serializer] | None = None
|
||||
""" Общий класс сериализатора для ответа """
|
||||
|
||||
def _get_serializer(self, serializer_class: Type[Serializer] | None, default: Type[Serializer] | None) -> Type[Serializer]:
|
||||
"""
|
||||
Возвращает класс сериализатора
|
||||
|
||||
:param serializer_class: Класс сериализатора
|
||||
:param default: Класс сериализатора по умолчанию
|
||||
|
||||
:return: Класс сериализатора
|
||||
"""
|
||||
|
||||
if serializer_class is not None:
|
||||
return serializer_class
|
||||
if default is not None:
|
||||
return default
|
||||
|
||||
raise AttributeError(f"{self.__class__.__name__} must define 'serializer' attribute")
|
||||
|
||||
@classmethod
|
||||
def _process_pk_param(cls, **kwargs: Any) -> UUID | None:
|
||||
"""
|
||||
Обработка параметра идентификатора объекта
|
||||
|
||||
:param kwargs: Именованные параметры
|
||||
|
||||
:return: Идентификатор объекта или None
|
||||
"""
|
||||
|
||||
result = kwargs.get("pk", None)
|
||||
if not result:
|
||||
raise BadRequestException()
|
||||
|
||||
return UUID(str(result))
|
||||
|
||||
def _process_query_params(self, serializer: Type[Serializer] | None, **kwargs: Any) -> dict[str, Any]:
|
||||
"""
|
||||
Обрабатывает параметры запроса
|
||||
|
||||
:param serializer: Сериализатор параметров
|
||||
:param kwargs: Именованные аргументы
|
||||
|
||||
:return: Параметры запроса
|
||||
"""
|
||||
|
||||
# Строим набор параметров
|
||||
result = {}
|
||||
params = list(self.request.query_params)
|
||||
for param in params:
|
||||
if param != 'request':
|
||||
result[param] = self.request.query_params.get(param)
|
||||
|
||||
# Добавляем именованные параметры
|
||||
result.update(kwargs)
|
||||
|
||||
# Если был задан сериализатор для параметров
|
||||
if serializer is not None:
|
||||
srz = serializer(data=self.request.query_params, context=self._get_request_context())
|
||||
srz.is_valid(raise_exception=True)
|
||||
return srz.validated_data
|
||||
|
||||
return result
|
||||
|
||||
def _process_data_params(self, serializer: Type[Serializer] | None) -> dict[str, Any]:
|
||||
"""
|
||||
Обрабатывает параметры данных запроса
|
||||
|
||||
:param serializer: Сериализатор параметров
|
||||
|
||||
:return: Параметры запроса
|
||||
"""
|
||||
|
||||
data = self.request.data
|
||||
|
||||
# Если был задан сериализатор для параметров
|
||||
if serializer is not None:
|
||||
partial = self._is_partial_update()
|
||||
srz = serializer(data=data, context=self._get_request_context(), partial=partial)
|
||||
srz.is_valid(raise_exception=True)
|
||||
return srz.validated_data
|
||||
|
||||
return data
|
||||
|
||||
def _is_partial_update(self) -> bool:
|
||||
"""
|
||||
Проверяет, является ли текущий запрос частичным обновлением
|
||||
|
||||
:return: True, если запрос является частичным обновлением
|
||||
"""
|
||||
|
||||
return self._get_request_method() == HttpMethod.PATCH
|
||||
|
||||
def _get_request_method(self) -> str:
|
||||
"""
|
||||
Возвращает имя метода HTTP протокола запроса
|
||||
|
||||
:return: Имя метода HTTP протокола запроса
|
||||
"""
|
||||
|
||||
method = self.request.method
|
||||
if not method:
|
||||
raise ImproperlyConfigured()
|
||||
|
||||
return method.upper()
|
||||
|
||||
def _get_request_session[T: SessionModel](self) -> T | None:
|
||||
"""
|
||||
Возвращает сессию, связанную с текущим запросом
|
||||
|
||||
:return: Сессия, связанная с текущим запросом
|
||||
"""
|
||||
|
||||
# noinspection PyTypeChecker
|
||||
session: SessionModel = self.request.auth
|
||||
return session
|
||||
|
||||
def _get_request_account[T: AccountModel](self) -> T:
|
||||
"""
|
||||
Возвращает аккаунт, связанный с текущим запросом
|
||||
|
||||
:return: Аккаунт, связанный с текущим запросом
|
||||
"""
|
||||
|
||||
session = self._get_request_session()
|
||||
if session is not None:
|
||||
|
||||
# noinspection PyTypeChecker
|
||||
account: T | None = session.account
|
||||
if account is not None:
|
||||
return account
|
||||
|
||||
raise UnauthorizedException()
|
||||
|
||||
def _get_request_context(self) -> dict[str, Any]:
|
||||
"""
|
||||
Возвращает контекст запроса
|
||||
|
||||
:return: Объект контекста запроса
|
||||
"""
|
||||
|
||||
try:
|
||||
account = self._get_request_account()
|
||||
except UnauthorizedException:
|
||||
account = None
|
||||
|
||||
return {
|
||||
"account": account,
|
||||
"request": self.request
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def get_paths(cls) -> list[URLPattern]:
|
||||
"""
|
||||
Возвращает полный список путей набора представлений
|
||||
|
||||
:return: Список путей
|
||||
"""
|
||||
|
||||
return cls._item_list_paths() + cls._item_detail_paths()
|
||||
|
||||
@classmethod
|
||||
def _path_method(cls, action: ViewSetAction) -> str:
|
||||
"""
|
||||
Возвращает имя метода для реализации действия представления
|
||||
|
||||
:param action: Действие
|
||||
|
||||
:return: Имя метода
|
||||
"""
|
||||
|
||||
return ViewSetAction.get_method(action)
|
||||
|
||||
@classmethod
|
||||
def _path_method_exists(cls, action: ViewSetAction) -> bool:
|
||||
"""
|
||||
Проверяет наличие метода для реализации действия представления
|
||||
|
||||
:param action: Действие
|
||||
|
||||
:return: True, если метод найден, иначе False
|
||||
"""
|
||||
|
||||
method_name = cls._path_method(action)
|
||||
if hasattr(cls, method_name):
|
||||
attr = getattr(cls, method_name)
|
||||
if callable(attr):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
def _item_list_paths(cls) -> list[URLPattern]:
|
||||
"""
|
||||
Возвращает список путей для работы со списком объектов
|
||||
|
||||
:return: Список путей
|
||||
"""
|
||||
|
||||
if cls.item_list_pattern is None:
|
||||
return []
|
||||
|
||||
args = {}
|
||||
if cls._path_method_exists(ViewSetAction.LIST):
|
||||
args["get"] = cls._path_method(ViewSetAction.LIST)
|
||||
if cls._path_method_exists(ViewSetAction.CREATE):
|
||||
args["post"] = cls._path_method(ViewSetAction.CREATE)
|
||||
|
||||
# Если есть путь, но нет методов
|
||||
if not args:
|
||||
raise ImproperlyConfigured("No methods defined for item_list_pattern.")
|
||||
|
||||
return [path(cls.item_list_pattern, cls.as_view(args))]
|
||||
|
||||
@classmethod
|
||||
def _item_detail_paths(cls) -> list[URLPattern]:
|
||||
"""
|
||||
Возвращает список путей для работы с объектом
|
||||
|
||||
:return: Список путей
|
||||
"""
|
||||
|
||||
if cls.item_detail_pattern is None:
|
||||
return []
|
||||
|
||||
args = {}
|
||||
if cls._path_method_exists(ViewSetAction.RETRIEVE):
|
||||
args["get"] = cls._path_method(ViewSetAction.RETRIEVE)
|
||||
if cls._path_method_exists(ViewSetAction.UPDATE):
|
||||
args["put"] = cls._path_method(ViewSetAction.UPDATE)
|
||||
if cls._path_method_exists(ViewSetAction.PARTIAL_UPDATE):
|
||||
args["patch"] = cls._path_method(ViewSetAction.PARTIAL_UPDATE)
|
||||
if cls._path_method_exists(ViewSetAction.DESTROY):
|
||||
args["delete"] = cls._path_method(ViewSetAction.DESTROY)
|
||||
|
||||
# Если есть путь, но нет методов
|
||||
if not args:
|
||||
raise ImproperlyConfigured("No methods defined for item_detail_pattern.")
|
||||
|
||||
return [path(cls.item_detail_pattern, cls.as_view(args))]
|
||||
92
net/xeaf/rack/core/views/view_set_create_mixin.py
Normal file
92
net/xeaf/rack/core/views/view_set_create_mixin.py
Normal file
@@ -0,0 +1,92 @@
|
||||
# DRF Rack
|
||||
# Библиотека классов расширений для Django REST Framework
|
||||
#
|
||||
# Автор: Николай В. Анохин <n.anokhin@xeaf.net>
|
||||
# Все права защищены. Лицензия: MIT
|
||||
|
||||
"""
|
||||
Описание класса ViewSetCreateMixin
|
||||
"""
|
||||
|
||||
from typing import Any
|
||||
from typing import Type
|
||||
|
||||
from django.db import transaction
|
||||
from rest_framework.exceptions import MethodNotAllowed
|
||||
from rest_framework.request import Request
|
||||
from rest_framework.serializers import Serializer
|
||||
|
||||
from net.xeaf.rack.core import CoreModel
|
||||
from net.xeaf.rack.core import CoreResponse
|
||||
from net.xeaf.rack.enums import HttpMethod
|
||||
from net.xeaf.rack.models.responses import DataResponse
|
||||
from .core_view_set import CoreViewSet
|
||||
|
||||
|
||||
class ViewSetCreateMixin(CoreViewSet):
|
||||
"""
|
||||
Реализует базовый функционал для создания объекта
|
||||
"""
|
||||
|
||||
create_param_serializer: Type[Serializer] | None = None
|
||||
""" Класс сериализатора для параметров запроса на создание объекта """
|
||||
|
||||
create_data_serializer: Type[Serializer] | None = None
|
||||
""" Класс сериализатора для данных запроса на создание объекта """
|
||||
|
||||
create_response_serializer: Type[Serializer] | None = None
|
||||
""" Класс сериализатора для ответа на запрос о создании объекта """
|
||||
|
||||
def item_create(self, request: Request, **kwargs: dict[str, Any]) -> CoreResponse:
|
||||
"""
|
||||
Обрабатывает HTTP запрос и возвращает ответ с новым объектом
|
||||
|
||||
:param request: Запрос
|
||||
:param kwargs: Именованные аргументы
|
||||
|
||||
:return: Ответ
|
||||
"""
|
||||
|
||||
params = self._process_create_params(**kwargs)
|
||||
with transaction.atomic():
|
||||
result = self._process_create(params)
|
||||
return self._process_create_response(result)
|
||||
|
||||
def _process_create(self, params: dict[str, Any]) -> dict[str, Any] | CoreModel:
|
||||
"""
|
||||
Обрабатывает создание объекта для формирования ответа
|
||||
|
||||
:param params: Параметры запроса
|
||||
|
||||
:return: Выбранный объект
|
||||
"""
|
||||
|
||||
raise MethodNotAllowed(method=HttpMethod.POST)
|
||||
|
||||
def _process_create_params(self, **kwargs: dict[str, Any]) -> dict[str, Any]:
|
||||
"""
|
||||
Разбирает и проверяет параметры запроса списка объектов
|
||||
|
||||
:param kwargs: Именованные аргументы
|
||||
|
||||
:return: Список параметров
|
||||
"""
|
||||
|
||||
query_params = self._process_query_params(self.create_param_serializer, **kwargs)
|
||||
data_params = self._process_data_params(self.create_data_serializer)
|
||||
|
||||
return {**query_params, **data_params}
|
||||
|
||||
def _process_create_response(self, data: dict[str, Any] | CoreModel) -> CoreResponse:
|
||||
"""
|
||||
Обрабатывает список объектов для формирования ответа
|
||||
|
||||
:param data: Список объектов
|
||||
|
||||
:return: Ответ для отправки
|
||||
"""
|
||||
|
||||
srz_class = self._get_serializer(self.create_response_serializer, self.response_serializer)
|
||||
srz = srz_class(data, many=False, context=self._get_request_context())
|
||||
|
||||
return DataResponse(data=srz.data)
|
||||
95
net/xeaf/rack/core/views/view_set_destroy_mixin.py
Normal file
95
net/xeaf/rack/core/views/view_set_destroy_mixin.py
Normal file
@@ -0,0 +1,95 @@
|
||||
# DRF Rack
|
||||
# Библиотека классов расширений для Django REST Framework
|
||||
#
|
||||
# Автор: Николай В. Анохин <n.anokhin@xeaf.net>
|
||||
# Все права защищены. Лицензия: MIT
|
||||
|
||||
"""
|
||||
Описание класса ViewSetDestroyMixin
|
||||
"""
|
||||
|
||||
import uuid
|
||||
from typing import Any
|
||||
from typing import Type
|
||||
|
||||
from django.db import transaction
|
||||
from rest_framework.exceptions import MethodNotAllowed
|
||||
from rest_framework.request import Request
|
||||
from rest_framework.serializers import Serializer
|
||||
|
||||
from net.xeaf.rack.core import CoreModel
|
||||
from net.xeaf.rack.core import CoreResponse
|
||||
from net.xeaf.rack.enums import HttpMethod
|
||||
from net.xeaf.rack.models.responses import DataResponse
|
||||
from net.xeaf.rack.models.responses import EmptyResponse
|
||||
from .core_view_set import CoreViewSet
|
||||
|
||||
|
||||
class ViewSetDestroyMixin(CoreViewSet):
|
||||
"""
|
||||
Реализует базовый функционал представления для удаления объекта
|
||||
"""
|
||||
|
||||
destroy_param_serializer: Type[Serializer] | None = None
|
||||
""" Класс сериализатора для параметров запроса объекта """
|
||||
|
||||
destroy_response_serializer: Type[Serializer] | None = None
|
||||
""" Класс сериализатора для ответа на запрос объекта """
|
||||
|
||||
def item_destroy(self, request: Request, **kwargs: dict[str, Any]) -> CoreResponse:
|
||||
"""
|
||||
Обрабатывает HTTP запрос и возвращает ответ с объектом
|
||||
|
||||
:param request: Запрос
|
||||
:param kwargs: Именованные аргументы
|
||||
|
||||
:return: Ответ
|
||||
"""
|
||||
|
||||
pk = self._process_pk_param(**kwargs)
|
||||
params = self._process_destroy_params(**kwargs)
|
||||
|
||||
with transaction.atomic():
|
||||
result = self._process_destroy(pk, params)
|
||||
return self._process_destroy_response(result)
|
||||
|
||||
def _process_destroy(self, pk: uuid.UUID | None, params: dict[str, Any]) -> dict[str, Any] | CoreModel | None:
|
||||
"""
|
||||
Обрабатывает удаление объекта
|
||||
|
||||
:param pk: Идентификатор объекта
|
||||
:param params: Параметры запроса
|
||||
|
||||
:return: Выбранный объект
|
||||
"""
|
||||
|
||||
raise MethodNotAllowed(method=HttpMethod.DELETE)
|
||||
|
||||
def _process_destroy_params(self, **kwargs: dict[str, Any]) -> dict[str, Any]:
|
||||
"""
|
||||
Разбирает и проверяет параметры запроса объекта
|
||||
|
||||
:param kwargs: Именованные аргументы
|
||||
|
||||
:return: Список параметров
|
||||
"""
|
||||
|
||||
return self._process_query_params(self.destroy_param_serializer, **kwargs)
|
||||
|
||||
def _process_destroy_response(self, data: dict[str, Any] | CoreModel | None) -> CoreResponse:
|
||||
"""
|
||||
Обрабатывает объект для формирования ответа
|
||||
|
||||
:param data: Список объектов
|
||||
|
||||
:return: Ответ для отправки
|
||||
"""
|
||||
|
||||
# Частный случай пустого ответа
|
||||
if data is None:
|
||||
return EmptyResponse()
|
||||
|
||||
srz_class = self._get_serializer(self.destroy_response_serializer, self.response_serializer)
|
||||
srz = srz_class(data, many=False, context=self._get_request_context())
|
||||
|
||||
return DataResponse(data=srz.data)
|
||||
85
net/xeaf/rack/core/views/view_set_list_mixin.py
Normal file
85
net/xeaf/rack/core/views/view_set_list_mixin.py
Normal file
@@ -0,0 +1,85 @@
|
||||
# DRF Rack
|
||||
# Библиотека классов расширений для Django REST Framework
|
||||
#
|
||||
# Автор: Николай В. Анохин <n.anokhin@xeaf.net>
|
||||
# Все права защищены. Лицензия: MIT
|
||||
|
||||
"""
|
||||
Описание класса ViewSetListMixin
|
||||
"""
|
||||
|
||||
from typing import Any
|
||||
from typing import Type
|
||||
|
||||
from rest_framework.exceptions import MethodNotAllowed
|
||||
from rest_framework.request import Request
|
||||
from rest_framework.serializers import Serializer
|
||||
|
||||
from net.xeaf.rack.core import CoreModel
|
||||
from net.xeaf.rack.core import CoreResponse
|
||||
from net.xeaf.rack.enums import HttpMethod
|
||||
from net.xeaf.rack.models.responses import ListResponse
|
||||
from .core_view_set import CoreViewSet
|
||||
|
||||
|
||||
class ViewSetListMixin(CoreViewSet):
|
||||
"""
|
||||
Реализует базовый функционал для выбора списков
|
||||
"""
|
||||
|
||||
list_param_serializer: Type[Serializer] | None = None
|
||||
""" Класс сериализатора для параметров запроса списка объектов """
|
||||
|
||||
list_response_serializer: Type[Serializer] | None = None
|
||||
""" Класс сериализатора для ответа на запрос списка объектов """
|
||||
|
||||
def item_list(self, request: Request, **kwargs: dict[str, Any]) -> CoreResponse:
|
||||
"""
|
||||
Обрабатывает HTTP запрос и возвращает ответ со списком объектов
|
||||
|
||||
:param request: Запрос
|
||||
:param kwargs: Именованные аргументы
|
||||
|
||||
:return: Ответ
|
||||
|
||||
"""
|
||||
|
||||
params = self._process_list_params(**kwargs)
|
||||
result = self._process_list(params)
|
||||
return self._process_list_response(result)
|
||||
|
||||
def _process_list(self, params: dict[str, Any]) -> list[dict[str, Any] | CoreModel]:
|
||||
"""
|
||||
Обрабатывает выбор списка объектов для формирования ответа
|
||||
|
||||
:param params: Параметры запроса
|
||||
|
||||
:return: Выбранный объект
|
||||
"""
|
||||
|
||||
raise MethodNotAllowed(method=HttpMethod.GET)
|
||||
|
||||
def _process_list_params(self, **kwargs: dict[str, Any]) -> dict[str, Any]:
|
||||
"""
|
||||
Разбирает и проверяет параметры запроса списка объектов
|
||||
|
||||
:param kwargs: Именованные аргументы
|
||||
|
||||
:return: Список параметров
|
||||
"""
|
||||
|
||||
return self._process_query_params(self.list_param_serializer, **kwargs)
|
||||
|
||||
def _process_list_response(self, data: list[dict[str, Any] | CoreModel]) -> CoreResponse:
|
||||
"""
|
||||
Обрабатывает список объектов для формирования ответа
|
||||
|
||||
:param data: Список объектов
|
||||
|
||||
:return: Ответ для отправки
|
||||
"""
|
||||
|
||||
srz_class = self._get_serializer(self.list_response_serializer, self.response_serializer)
|
||||
srz = srz_class(data, many=True, context=self._get_request_context())
|
||||
|
||||
return ListResponse(data=list(srz.data))
|
||||
88
net/xeaf/rack/core/views/view_set_retrieve_mixin.py
Normal file
88
net/xeaf/rack/core/views/view_set_retrieve_mixin.py
Normal file
@@ -0,0 +1,88 @@
|
||||
# DRF Rack
|
||||
# Библиотека классов расширений для Django REST Framework
|
||||
#
|
||||
# Автор: Николай В. Анохин <n.anokhin@xeaf.net>
|
||||
# Все права защищены. Лицензия: MIT
|
||||
|
||||
"""
|
||||
Описание класса ViewSetUpdateMixin
|
||||
"""
|
||||
|
||||
import uuid
|
||||
from typing import Any
|
||||
from typing import Type
|
||||
|
||||
from rest_framework.exceptions import MethodNotAllowed
|
||||
from rest_framework.request import Request
|
||||
from rest_framework.serializers import Serializer
|
||||
|
||||
from net.xeaf.rack.core import CoreModel
|
||||
from net.xeaf.rack.core import CoreResponse
|
||||
from net.xeaf.rack.enums import HttpMethod
|
||||
from net.xeaf.rack.models.responses import DataResponse
|
||||
from .core_view_set import CoreViewSet
|
||||
|
||||
|
||||
class ViewSetRetrieveMixin(CoreViewSet):
|
||||
"""
|
||||
Реализует базовый функционал представления для выбора объекта
|
||||
"""
|
||||
|
||||
retrieve_param_serializer: Type[Serializer] | None = None
|
||||
""" Класс сериализатора для параметров запроса объекта """
|
||||
|
||||
retrieve_response_serializer: Type[Serializer] | None = None
|
||||
""" Класс сериализатора для ответа на запрос объекта """
|
||||
|
||||
def item_retrieve(self, request: Request, **kwargs: dict[str, Any]) -> CoreResponse:
|
||||
"""
|
||||
Обрабатывает HTTP запрос и возвращает ответ с объектом
|
||||
|
||||
:param request: Запрос
|
||||
:param kwargs: Именованные аргументы
|
||||
|
||||
:return: Ответ
|
||||
"""
|
||||
|
||||
pk = self._process_pk_param(**kwargs)
|
||||
params = self._process_retrieve_params(**kwargs)
|
||||
|
||||
result = self._process_retrieve(pk, params)
|
||||
return self._process_retrieve_response(result)
|
||||
|
||||
def _process_retrieve(self, pk: uuid.UUID | None, params: dict[str, Any]) -> dict[str, Any] | CoreModel:
|
||||
"""
|
||||
Обрабатывает выбор объекта для формирования ответа
|
||||
|
||||
:param pk: Идентификатор объекта
|
||||
:param params: Параметры запроса
|
||||
|
||||
:return: Выбранный объект
|
||||
"""
|
||||
|
||||
raise MethodNotAllowed(method=HttpMethod.GET)
|
||||
|
||||
def _process_retrieve_params(self, **kwargs: dict[str, Any]) -> dict[str, Any]:
|
||||
"""
|
||||
Разбирает и проверяет параметры запроса объекта
|
||||
|
||||
:param kwargs: Именованные аргументы
|
||||
|
||||
:return: Список параметров
|
||||
"""
|
||||
|
||||
return self._process_query_params(self.retrieve_param_serializer, **kwargs)
|
||||
|
||||
def _process_retrieve_response(self, data: dict[str, Any] | CoreModel) -> CoreResponse:
|
||||
"""
|
||||
Обрабатывает объект для формирования ответа
|
||||
|
||||
:param data: Список объектов
|
||||
|
||||
:return: Ответ для отправки
|
||||
"""
|
||||
|
||||
srz_class = self._get_serializer(self.retrieve_response_serializer, self.response_serializer)
|
||||
srz = srz_class(data, many=False, context=self._get_request_context())
|
||||
|
||||
return DataResponse(data=srz.data)
|
||||
107
net/xeaf/rack/core/views/view_set_update_mixin.py
Normal file
107
net/xeaf/rack/core/views/view_set_update_mixin.py
Normal file
@@ -0,0 +1,107 @@
|
||||
# DRF Rack
|
||||
# Библиотека классов расширений для Django REST Framework
|
||||
#
|
||||
# Автор: Николай В. Анохин <n.anokhin@xeaf.net>
|
||||
# Все права защищены. Лицензия: MIT
|
||||
|
||||
"""
|
||||
Описание класса ViewSetUpdateMixin
|
||||
"""
|
||||
|
||||
import uuid
|
||||
from typing import Any
|
||||
from typing import Type
|
||||
|
||||
from django.db import transaction
|
||||
from rest_framework.exceptions import MethodNotAllowed
|
||||
from rest_framework.request import Request
|
||||
from rest_framework.serializers import Serializer
|
||||
|
||||
from net.xeaf.rack.core import CoreModel
|
||||
from net.xeaf.rack.core import CoreResponse
|
||||
from net.xeaf.rack.models.responses import DataResponse
|
||||
from .core_view_set import CoreViewSet
|
||||
|
||||
|
||||
class ViewSetUpdateMixin(CoreViewSet):
|
||||
"""
|
||||
Реализует базовый функционал представления для изменения объекта
|
||||
"""
|
||||
|
||||
update_param_serializer: Type[Serializer] | None = None
|
||||
""" Класс сериализатора для параметров запроса на изменение объекта """
|
||||
|
||||
update_data_serializer: Type[Serializer] | None = None
|
||||
""" Класс сериализатора для данных запроса на изменение объекта """
|
||||
|
||||
update_response_serializer: Type[Serializer] | None = None
|
||||
""" Класс сериализатора для ответа на запрос на изменение объекта """
|
||||
|
||||
def item_update(self, request: Request, **kwargs: dict[str, Any]) -> CoreResponse:
|
||||
"""
|
||||
Обрабатывает HTTP запрос и возвращает ответ с объектом
|
||||
|
||||
:param request: Запрос
|
||||
:param kwargs: Именованные аргументы
|
||||
|
||||
:return: Ответ
|
||||
"""
|
||||
|
||||
pk = self._process_pk_param(**kwargs)
|
||||
params = self._process_update_params(**kwargs)
|
||||
|
||||
with transaction.atomic():
|
||||
result = self._process_update(pk, params)
|
||||
return self._process_update_response(result)
|
||||
|
||||
def item_partial_update(self, request: Request, **kwargs: dict[str, Any]) -> CoreResponse:
|
||||
"""
|
||||
Обрабатывает HTTP запрос и возвращает ответ с измененным объектом
|
||||
|
||||
:param request: Запрос
|
||||
:param kwargs: Именованные аргументы
|
||||
|
||||
:return: Ответ
|
||||
"""
|
||||
|
||||
return self.item_update(request, **kwargs)
|
||||
|
||||
def _process_update(self, pk: uuid.UUID | None, params: dict[str, Any]) -> dict[str, Any] | CoreModel:
|
||||
"""
|
||||
Обрабатывает выбор и изменение объекта для формирования ответа
|
||||
|
||||
:param pk: Идентификатор объекта
|
||||
:param params: Параметры запроса
|
||||
|
||||
:return: Выбранный объект
|
||||
"""
|
||||
|
||||
raise MethodNotAllowed(method=self._get_request_method())
|
||||
|
||||
def _process_update_params(self, **kwargs: dict[str, Any]) -> dict[str, Any]:
|
||||
"""
|
||||
Разбирает и проверяет параметры запроса объекта
|
||||
|
||||
:param kwargs: Именованные аргументы
|
||||
|
||||
:return: Список параметров
|
||||
"""
|
||||
|
||||
query_params = self._process_query_params(self.update_param_serializer, **kwargs)
|
||||
data_params = self._process_data_params(self.update_data_serializer)
|
||||
|
||||
return {**query_params, **data_params}
|
||||
|
||||
def _process_update_response(self, data: dict[str, Any] | CoreModel) -> CoreResponse:
|
||||
"""
|
||||
Обрабатывает объект для формирования ответа
|
||||
|
||||
:param data: Список объектов
|
||||
|
||||
:return: Ответ для отправки
|
||||
"""
|
||||
|
||||
srz_class = self._get_serializer(self.update_response_serializer, self.response_serializer)
|
||||
srz = srz_class(data, many=False, context=self._get_request_context())
|
||||
|
||||
return DataResponse(data=srz.data)
|
||||
Reference in New Issue
Block a user