Оставлю это здесь, как черновик.
drf-spectacular плохо дружит с SerializerMethodField. Поэтому я написал свой класс поля.
from typing import Type, cast, TypeVar
from uuid import uuid4
from rest_framework.fields import Field
from rest_framework.serializers import ListSerializer, Serializer
SerializerType = TypeVar("SerializerType", bound=Serializer)
class DataSerializerField(Field):
"""A serializer for calculated data"""
def __new__(cls, serializer: Type['SerializerType'], *args, method_name=None, **kwargs) -> 'SerializerType':
class GetAttributeMixin(Field if TYPE_CHECKING else object):
def get_attribute(self, instance):
if self.source_attrs:
value = super().get_attribute(instance)
else:
default_method_name = f'get_{self.field_name}_data'
attr_name = method_name or default_method_name
method = getattr(self.parent, attr_name, None)
if not method:
raise AttributeError(f'Method {attr_name} not found in class {type(self.parent).__name__}')
value = method(instance)
return value
class_name = f'{serializer.__name__.replace("Serializer", "")}DataSerializer'
list_serializer_class = type('DataListSerializer', (GetAttributeMixin, ListSerializer), {})
parent_meta_class = getattr(serializer, 'Meta', object)
ref_name = f'{class_name}_{uuid4().hex}'
meta_class = type(
'Meta',
(parent_meta_class,),
{'list_serializer_class': list_serializer_class, 'ref_name': ref_name},
)
data_serializer_class = type(class_name, (GetAttributeMixin, serializer), {'Meta': meta_class})
kwargs.setdefault('source', '*')
kwargs['read_only'] = True
kwargs['required'] = False
return data_serializer_class(**kwargs)
Пример использования:
class ParentSerializer(Serializer):
serializer_field_object = DataSerializerField(ChildSerializer)
serializer_field_list = DataSerializerField(ChildSerializer, many=True)
def get_serializer_field_object_data(parent_obj) -> ChildObject:
return ChildObject()
def get_serializer_field_list_data(parent_obj) -> list[ChildObject]:
return [ChildObject(), ChildObject()]
Аннотацию результатов методов можно не писать, она влияет только на работу линтеров.
Этот же код с SerializerMethodField выглядел бы так
class ParentSerializer(Serializer):
serializer_field_object = SerializerMethodField()
serializer_field_list = SerializerMethodField()
def get_serializer_field_object(parent_obj) -> ChildSerializer:
return ChildSerializer(ChildObject()).data
def get_serializer_field_list(parent_obj) -> list[ChildSerializer]:
return ChildSerializer([ChildObject(), ChildObject()]).data
Обратите внимание на аннотацию результатов функций. Она нужна для того, чтобы spectacular-swagger мог правильно отобразить схему. При этом аннотация не соответствует реальному типу возвращаемых данных.
Top comments (0)