0

drf-yasg我有一个用于生成swagger.json文件的 Django(Django Rest Framework)网络服务。在模型中,我有几个枚举/选择字段用于多个地方。默认情况下,drf-yasg为每个事件定义内联字段:

Choices = serializers.ChoiceField(choices=['a', 'b', 'c'])

class SomeObject(serializers.Serializer):
    field_1 = Choices
    field_2 = Choices

在 swagger 文件中生成以下定义:

{
  "definitions": {
    "SomeObject": {
      "required": [ "field_1", "field_2" ],
      "type": "object",
      "properties": {
        "field_1": {
          "title": "Field 1",
          "type": "string",
          "enum": [ "a", "b", "c" ]
        },
        "field_2": {
          "title": "Field 1",
          "type": "string",
          "enum": [ "a", "b", "c" ]
        }
      }
    }
  }
}

这是一个小问题,因为它使客户端代码生成工具生成每个枚举作为自己的类型,而不是重用定义。因此,我想创建一个像这样的 swaggerfile:

{
  "definitions": {
    "Choices": {
      "title": "Field 1",
      "type": "string",
      "enum": [ "a", "b", "c" ]
    },
    "SomeObject": {
      "required": [ "field_1", "field_2" ],
      "type": "object",
      "properties": {
        "field_1": {
          "$ref": "#/definitions/Choices"
        },
        "field_2": {
          "$ref": "#/definitions/Choices"
        }
      }
    }
  }
}

是否可以在 Django Rest Framework 中启用此行为?

4

1 回答 1

0

万一有人看到这一点并需要一些指示。我最终实现了如下:

创建一个子类ChoiceField,用于指示枚举应该作为引用实现,以及其他一些检查。检查str是为了确保序列化知道如何处理这些值:

from rest_framework import serializers

from enum import Enum

class ReferenceEnumField(serializers.ChoiceField):
  def __init__(self, enum_type, **kwargs):
    if not issubclass(enum_type, str):
      raise TypeError("enum_type should inherit from str in order to be json-serializable.")
    if not issubclass(enum_type, Enum):
      raise TypeError("enum_type should be an Enum")
    self.enum_name = enum_type.__name__
    super().__init__(choices=[enum.name for enum in enum_type], **kwargs)

然后是可以添加到装饰器中的检查器,如下所示:

from drf_yasg.inspectors.base import NotHandled
from drf_yasg.inspectors.field import ReferencingSerializerInspector
from drf_yasg import openapi
from drf_yasg.errors import SwaggerGenerationError

from .ReferenceEnumfield import ReferenceEnumField

class EnumAsReferenceInspector(ReferencingSerializerInspector):
  accepting_objects = True

  @classmethod
  def set_accepting_objects(cls, value):
    cls.accepting_objects = value

  def field_to_swagger_object(self, field, swagger_object_type, use_references, **kwargs):
    SwaggerType, ChildSwaggerType = self._get_partial_types(field, swagger_object_type, use_references, **kwargs)


    if EnumAsReferenceInspector.accepting_objects and isinstance(field, ReferenceEnumField):
      try:
        # Avoid infinite recursion by setting the class to not accept objects to serialize.
        EnumAsReferenceInspector.set_accepting_objects(False)
        if swagger_object_type != openapi.Schema:
          raise SwaggerGenerationError("cannot instantiate nested serializer as " + swagger_object_type.__name__)

        ref_name = field.enum_name

        def make_schema_definition(enum=field):
          return self.probe_field_inspectors(enum, ChildSwaggerType, use_references)
        if not ref_name or not use_references:
          return make_schema_definition()

        definitions = self.components.with_scope(openapi.SCHEMA_DEFINITIONS)
        actual_schema = definitions.setdefault(ref_name, make_schema_definition)
        actual_schema._remove_read_only()

        return openapi.SchemaRef(definitions, ref_name)
      finally:
        EnumAsReferenceInspector.set_accepting_objects(True)

    return NotHandled

它是从库中的其他一些代码拼凑而成的,所以我不确定是否有可以省略或以不同方式完成的行,但它可以解决问题。

于 2019-11-25T15:11:12.290 回答