Оставлю это здесь, как черновик.
drf-spectacular плохо дружит с SerializerMethodField. Поэтому я написал свой класс поля.
Внимание в коде используется mixin_for
class DataSerializerField(Field):
"""A serializer for calculated data"""
def __new__(
cls, serializer: Type["SerializerType"], *args, method_name=None, **kwargs
) -> "SerializerType":
class GetAttributeMixin(mixin_for(Field)):
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 = class_name + now_as_timestamp_str()
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)