简体   繁体   English

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

[英]With drf-yasg, how can I support multiple serializers in the Response?

With a response from my drf just containing the data given by a single serializer, we can implement it as:我的 drf 的响应仅包含单个序列化程序给出的数据,我们可以将其实现为:

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

Which works fantastic, but with some requests constructing a dictionary, where two or three of the keys correspond to different serializers, eg效果很好,但是有些请求构建了一个字典,其中两个或三个键对应于不同的序列化程序,例如

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

How can we describe that in the auto schema?我们如何在自动模式中描述它? I've tried a few different approaches, mostly similar to the following:我尝试了几种不同的方法,大多类似于以下方法:

@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)
            })
    )}
)

But always fails when loading the documentation, with flex saying:但是在加载文档时总是失败, 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):

I've read the documentation over and over again, and scoured over github for an example, but I couldn't find an example or anyone doing this.我一遍又一遍地阅读文档,并在 github 上搜索了一个例子,但我找不到一个例子或任何人这样做。 So my question is how to successfully manually define a schema for a response that contains different serializers for different keys in the returned response?所以我的问题是如何成功为响应手动定义架构,该架构包含针对返回响应中不同键的不同序列化程序?

What I usually do is to create another serializer (just so that drf-yasg can generate the docs).我通常做的是创建另一个序列化程序(只是为了让 drf-yasg 可以生成文档)。

For example if I have an endpoint that returns:例如,如果我有一个返回的端点:

{
   "results": [..list of serialized results with serializer X...]
}

I create a second serializer:我创建了第二个序列化程序:

class Y(serializers.Serializer):
    results = X(many=True)

and use Y serializer in the swagger_auto_schema decorator.并在 swagger_auto_schema 装饰器中使用Y序列化器。

I ended up being able to do it, although probably not the most elegant solution but it does work.我最终能够做到这一点,虽然可能不是最优雅的解决方案,但它确实有效。

My drf has a custom app-label format, so all my apps are in a folder, and let's call this folder apps .我的 drf 有一个自定义的应用程序标签格式,所以我所有的应用程序都在一个文件夹中,我们称这个文件夹为apps

In my question, for a serializer, we can replace Serializer4ModelA in the properties section of the openapi.Schema with a custom function, lets say get_serializer(Serializer4ModelA()) .在我的问题,对于一个串行器,我们可以更换Serializer4ModelAproperties的部分openapi.Schema用自定义功能,让说get_serializer(Serializer4ModelA())

So my idea was to basically construct the schema myself by getting the information automatically and automatically constructing the properties dictionary.所以我的想法是通过自动获取信息并自动构建properties字典来基本上自己构建模式。 It's very hacky, but useful for me because in my documentation I also want to pass in the serializers for Dynamodb, so I made a very similar function for Dynamodb serializers.它非常hacky,但对我很有用,因为在我的文档中,我还想传入 Dynamodb 的序列化程序,所以我为 Dynamodb 序列化程序创建了一个非常相似的函数。

I only just made it, and it works, but obviously needs more attention to cover all fields in the field mapping , better dealing with SerializerMethodFields .我才刚刚实现,它可以工作,但显然需要更多关注覆盖field mapping中的所有字段,更好地处理SerializerMethodFields

But none the less, it is a solution that works but is not generic, tweaks and stuff will have to be made depending on your particular project.但无论如何,这是一个有效但不通用的解决方案,必须根据您的特定项目进行调整和其他内容。

I implemented the function roughly as follows:我大致实现了这个功能如下:

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

I faced this problem and was looking if there is another way than my initial solution (same as how @Hernan explained it) but found none.我遇到了这个问题,正在寻找除了我最初的解决方案之外是否还有其他方法(与@Hernan 的解释相同),但没有找到。 The code of drf_yasg.openapi.Schema (drf_yasg==1.20.0) showed that it doesn't accept any serializer object. drf_yasg.openapi.Schema (drf_yasg==1.20.0) 的代码表明它不接受任何序列化程序对象。 So as already said by @Hernan, the way around this is to have an additional serializer and define there the nested child serializers.所以正如@Hernan 已经说过的,解决这个问题的方法是有一个额外的序列化程序并在那里定义嵌套的子序列化程序。 Then, pass it to either the swagger_auto_schema.responses directly or through an openapi.Response.schema (as below):然后,将其直接或通过openapi.Response.schema (如下)传递给swagger_auto_schema.responses

from django.urls import path
from drf_yasg import openapi
from drf_yasg.utils import swagger_auto_schema
from rest_framework import serializers, status, views


class Serializer4ModelA(serializers.Serializer):
    dog = serializers.CharField(label="My dog is a good boy")
class Serializer4ModelB(serializers.Serializer):
    perro = serializers.CharField(label="Mi perro es un buen chico")
    hund = serializers.CharField(label="Mein Hund ist ein guter Junge")
    aso = serializers.CharField(label="Ang aso ko ay mabait na bata")
class Serializer4ModelC(serializers.Serializer):
    eey = serializers.CharField(label="Eygaygu waa wiil fiican")
class SampleResponseSerializer(serializers.Serializer):
    a = Serializer4ModelA(many=True)
    b = Serializer4ModelB(many=True)
    c = Serializer4ModelC(many=True)


class SampleView(views.APIView):
    @swagger_auto_schema(
        responses={
            status.HTTP_200_OK: openapi.Response(
                description="response description",
                schema=SampleResponseSerializer,
            )
        }
    )
    def get(self, request):
        pass

urlpatterns = [
    path("sample/", SampleView.as_view()),
]

Output:输出: 在此处输入图片说明

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM