С помощью drf-yasg, как я могу поддерживать несколько сериализаторов в Response? - PullRequest
0 голосов
/ 18 декабря 2018

С ответом от моего drf, содержащего только данные, предоставленные одним сериализатором, мы можем реализовать его как:

@swagger_auto_schema(
    operation_id='ID example',
    operation_description="Description example.",
    responses={status.HTTP_200_OK: Serializer4ModelA(many=True)},
)

, который работает фантастически, но с некоторыми запросами, создающими словарь, где два или триклавиш соответствуют различным сериализаторам, например,

response = {
    "a": serializer_data_for_model_a,
    "b": serializer_data_for_model_b,
    "c": serializer_data_for_model_c
}

Как мы можем описать это в автоматической схеме?Я пробовал несколько разных подходов, в основном похожих на следующие:

@swagger_auto_schema(
    operation_id='ID example',
    operation_description="Description example.",
    responses={status.HTTP_200_OK: openapi.Response(
        description='response description',
        schema=openapi.Schema(
            type=openapi.TYPE_OBJECT,
            properties={
                'a': Serializer4ModelA(many=True),
                'b': Serializer4ModelB(many=True),
                'c': Serializer4ModelC(many=True)
            })
    )}
)

Но всегда происходит сбой при загрузке документации, с flex высказыванием:

"/usr/local/lib/python3.6/site-packages/flex/utils.py", line 125, in 
get_type_for_value raise ValueError("Unable to identify type of 
{0}".format(repr(value)))
ValueError: Unable to identify type of 
Serializer4ModelA(many=True):

Я имеюПрочитайте документацию снова и снова, и поищите в github пример, но я не смог найти пример или кто-то, кто делает это.Поэтому мой вопрос заключается в том, как успешно вручную определить схему для ответа, который содержит разные сериализаторы для разных ключей в возвращаемом ответе?

1 Ответ

0 голосов
/ 30 декабря 2018

В итоге я смог это сделать, хотя, возможно, это не самое элегантное решение, но оно действительно работает.

Мой drf имеет собственный формат меток приложения, поэтому все мои приложения находятся в папке, идавайте назовем эту папку apps.

В моем вопросе для сериализатора мы можем заменить Serializer4ModelA в секции properties в openapi.Schema пользовательской функцией, скажем, get_serializer(Serializer4ModelA()).

Таким образом, моя идея состояла в том, чтобы в принципе создать схему самостоятельно, автоматически получая информацию и автоматически создавая словарь properties.Это очень забавно, но полезно для меня, потому что в моей документации я также хочу передать сериализаторы для Dynamodb, поэтому я сделал очень похожую функцию для сериализаторов Dynamodb.

Я только что сделал это, и это работает,но, очевидно, требуется больше внимания, чтобы охватить все поля в field mapping, лучше иметь дело с SerializerMethodFields.

Но тем не менее, это решение, которое работает, но не является общим, твики и прочее придетсябыть сделано в зависимости от вашего конкретного проекта.

Я реализовал функцию примерно следующим образом:

from drf_yasg import openapi
from drf_yasg.inspectors import SwaggerAutoSchema
from drf_yasg.utils import swagger_auto_schema
from drf_yasg.inspectors import FieldInspector
from drf_yasg.utils import swagger_serializer_method
import rest_framework

rest_framework_openapi_field_mapping = {
     "ListField": openapi.TYPE_ARRAY,
     "CharField": openapi.TYPE_STRING,
     "BooleanField": openapi.TYPE_BOOLEAN,
     "FloatField": openapi.TYPE_NUMBER,
     "DateTimeField": openapi.TYPE_STRING,
     "IntegerField": openapi.TYPE_INTEGER,
     "SerializerMethodField": openapi.TYPE_STRING
}

def parse_rest_framework_field(field):
    rest_framework_field_type = field.split("(")[0]
    openapi_field_type = 
    rest_framework_openapi_field_mapping[rest_framework_field_type]
    if "help_text=" in field:
        field_description = field.split("help_text='")[-1].split("'")[0]
    else:
        field_description = None
    return openapi.Schema(type=openapi_field_type, description=field_description)

def parse_serializer(serializer):
    properties = {}
    for k,v in serializer.get_fields().items():
        if v.__module__ == "rest_framework.fields":
            properties[k] = parse_rest_framework_field(str(v))
        elif v.__module__.startswith("apps."):
            serializer = str(v).strip().split("(")[0]
            exec(f"from {v.__module__} import {serializer}")
            eval_serializer = eval(f"{serializer}()")
            properties[k] = openapi.Schema(type=openapi.TYPE_OBJECT, properties=parse_serializer(eval_serializer))
        else:
            pass
    return properties

def get_serializer(serializer, description):
    """ Needs to return openapi.Schema() """
    properties = parse_serializer(serializer)
    return_openapi_schema = openapi.Schema( type=openapi.TYPE_OBJECT, properties=properties, description=description)
    return return_openapi_schema
...