使用 drf-yasg,如何在响应中支持多个序列化程序?

jup*_*iar 5 django swagger django-rest-framework

我的 drf 的响应仅包含单个序列化程序给出的数据,我们可以将其实现为:

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

效果很好,但是有些请求构建了一个字典,其中两个或三个键对应于不同的序列化程序,例如

response = {
    "a": serializer_data_for_model_a,
    "b": serializer_data_for_model_b,
    "c": serializer_data_for_model_c
}
Run Code Online (Sandbox Code Playgroud)

我们如何在自动模式中描述它?我尝试了几种不同的方法,大多类似于以下方法:

@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)
            })
    )}
)
Run Code Online (Sandbox Code Playgroud)

但是在加载文档时总是失败,并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):
Run Code Online (Sandbox Code Playgroud)

我一遍又一遍地阅读文档,并在 github 上搜索了一个例子,但我找不到一个例子或任何人这样做。所以我的问题是如何成功为响应手动定义架构,该架构包含针对返回响应中不同键的不同序列化程序?

jup*_*iar 1

我最终能够做到这一点,虽然可能不是最优雅的解决方案,但它确实有效。

我的 drf 有自定义应用程序标签格式,因此我的所有应用程序都在一个文件夹中,我们将此文件夹称为apps

在我的问题中,对于序列化器,我们可以Serializer4ModelA用自定义函数替换properties的 部分openapi.Schema,比如说get_serializer(Serializer4ModelA())

所以我的想法是通过自动获取信息并自动构建properties字典来基本上自己构建模式。它非常 hacky,但对我很有用,因为在我的文档中,我还想传递 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
Run Code Online (Sandbox Code Playgroud)