架构

架构

可供机器读取的 [架构] 描述了可通过 API 获得的资源、它们的 URL、它们的表示方式以及它们支持的操作。

— Heroku,Heroku 平台 API 的 JSON 架构


弃用通知

REST 框架内置的 OpenAPI 架构生成支持已弃用,取而代之的是可以提供此功能的第三方软件包。内置支持将移至一个单独的软件包,然后在后续版本中逐步淘汰。

作为全面替代,我们推荐使用 drf-spectacular 软件包。它广泛支持从 REST 框架 API 生成 OpenAPI 3 架构,并提供了自动和可自定义选项。有关详细信息,请参阅 记录您的 API


API 架构是一个有用的工具,允许一系列用例,包括生成参考文档或驱动可以与您的 API 交互的动态客户端库。

Django REST 框架提供对 OpenAPI 架构自动生成的支持。

概述

架构生成有几个活动部分。值得概述一下

  • SchemaGenerator 是一个顶级类,负责遍历您配置的 URL 模式,查找 APIView 子类,查询它们的架构表示,并编译最终架构对象。
  • AutoSchema 封装了每个视图架构内省所需的所有详细信息。通过 schema 属性附加到每个视图。您对 AutoSchema 进行子类化以自定义您的架构。
  • generateschema 管理命令允许您离线生成静态架构。
  • 或者,您可以路由 SchemaView 以动态生成和提供您的架构。
  • settings.DEFAULT_SCHEMA_CLASS 允许您指定一个 AutoSchema 子类作为您项目的默认值。

以下部分将进行更多解释。

生成 OpenAPI 架构

安装依赖项

pip install pyyaml uritemplate inflection
  • pyyaml 用于将架构生成到基于 YAML 的 OpenAPI 格式中。
  • uritemplate 在内部用于获取路径中的参数。
  • inflection 用于在列表端点中更恰当地使操作复数化。

使用 generateschema 管理命令生成静态架构

如果架构是静态的,则可以使用 generateschema 管理命令

./manage.py generateschema --file openapi-schema.yml

以这种方式生成架构后,可以使用架构生成器无法自动推断的任何其他信息对其进行注释。

你可能希望将 API 架构检入版本控制中,并使用每个新版本对其进行更新,或从网站的静态媒体中提供 API 架构。

使用 SchemaView 生成动态架构

如果你需要动态架构,例如,因为外键选择取决于数据库值,则可以路由一个 SchemaView,该视图将按需生成和提供架构。

要路由 SchemaView,请使用 get_schema_view() 帮助器。

urls.py

from rest_framework.schemas import get_schema_view

urlpatterns = [
    # ...
    # Use the `get_schema_view()` helper to add a `SchemaView` to project URLs.
    #   * `title` and `description` parameters are passed to `SchemaGenerator`.
    #   * Provide view name for use with `reverse()`.
    path(
        "openapi",
        get_schema_view(
            title="Your Project", description="API for all things …", version="1.0.0"
        ),
        name="openapi-schema",
    ),
    # ...
]

get_schema_view()

get_schema_view() 帮助器采用以下关键字参数

  • title:可用于为架构定义提供描述性标题。
  • description:较长的描述性文本。
  • version:API 的版本。
  • url:可用于为架构传递规范基本 URL。

    schema_view = get_schema_view(
        title='Server Monitoring API',
        url='https://www.example.org/api/'
    )
    
  • urlconf:表示要为其生成 API 架构的 URL conf 的导入路径的字符串。这默认为 Django 的 ROOT_URLCONF 设置的值。

    schema_view = get_schema_view(
        title='Server Monitoring API',
        url='https://www.example.org/api/',
        urlconf='myproject.urls'
    )
    
  • patterns:用于将架构自省限制为的 URL 模式列表。如果你只想在架构中公开 myproject.api URL

    schema_url_patterns = [
        path('api/', include('myproject.api.urls')),
    ]
    
    schema_view = get_schema_view(
        title='Server Monitoring API',
        url='https://www.example.org/api/',
        patterns=schema_url_patterns,
    )
    
    • public:可用于指定架构是否应绕过视图权限。默认为 False
  • generator_class:可用于指定要传递给 SchemaViewSchemaGenerator 子类。

  • authentication_classes:可用于指定将应用于架构端点的身份验证类列表。默认为 settings.DEFAULT_AUTHENTICATION_CLASSES
  • permission_classes:可用于指定将应用于架构端点的权限类列表。默认为 settings.DEFAULT_PERMISSION_CLASSES
  • renderer_classes:可用于传递可用于呈现 API 根端点的渲染器类集。

SchemaGenerator

架构级自定义

from rest_framework.schemas.openapi import SchemaGenerator

SchemaGenerator 是一个类,它遍历路由的 URL 模式列表,请求每个视图的架构,并整理结果 OpenAPI 架构。

通常你不需要自己实例化 SchemaGenerator,但你可以像这样

generator = SchemaGenerator(title='Stock Prices API')

参数

  • title 必需:API 的名称。
  • description:较长的描述性文本。
  • version:API 的版本。默认为 0.1.0
  • url:API 架构的根 URL。除非架构包含在路径前缀下,否则不需要此选项。
  • patterns:生成架构时要检查的 URL 列表。默认为项目的 URL 配置。
  • urlconf:生成架构时要使用的 URL 配置模块名称。默认为 settings.ROOT_URLCONF

为了自定义顶级架构,请对 rest_framework.schemas.openapi.SchemaGenerator 进行子类化,并将子类作为参数提供给 generateschema 命令或 get_schema_view() 帮助函数。

get_schema(self, request=None, public=False)

返回表示 OpenAPI 架构的字典

generator = SchemaGenerator(title='Stock Prices API')
schema = generator.get_schema()

request 参数是可选的,如果你想对生成的架构应用按用户权限,可以使用此参数。

如果你想自定义生成的字典,这是一个很好的重写点。例如,你可能希望向 顶级 info 对象 添加服务条款

class TOSSchemaGenerator(SchemaGenerator):
    def get_schema(self, *args, **kwargs):
        schema = super().get_schema(*args, **kwargs)
        schema["info"]["termsOfService"] = "https://example.com/tos.html"
        return schema

AutoSchema

按视图自定义

from rest_framework.schemas.openapi import AutoSchema

默认情况下,视图内省由 AutoSchema 实例执行,可通过 APIView 上的 schema 属性访问。

auto_schema = some_view.schema

AutoSchema 提供了每个视图、请求方法和路径所需的 OpenAPI 元素

  • OpenAPI 组件 列表。在 DRF 术语中,这些是描述请求和响应正文的序列化程序映射。
  • 描述端点的适当 OpenAPI 操作对象,包括分页、过滤等的路径和查询参数。
components = auto_schema.get_components(...)
operation = auto_schema.get_operation(...)

在编译架构时,SchemaGenerator 会为每个视图、允许的方法和路径调用 get_components()get_operation()


注意:组件的自动内省和许多操作参数依赖于 GenericAPIView 的相关属性和方法:get_serializer()pagination_classfilter_backends 等。对于基本的 APIView 子类,由于这个原因,默认内省基本上仅限于 URL 关键字参数路径参数。


AutoSchema 封装了架构生成所需的视图内省。因此,所有架构生成逻辑都保存在一个地方,而不是分散在已经广泛的视图、序列化程序和字段 API 中。

遵循此模式,在自定义架构生成时,尽量不要让架构逻辑泄漏到自己的视图、序列化程序或字段中。你可能会倾向于做类似这样的事情

class CustomSchema(AutoSchema):
    """
    AutoSchema subclass using schema_extra_info on the view.
    """

    ...


class CustomView(APIView):
    schema = CustomSchema()
    schema_extra_info = ...  # some extra info

在这里,AutoSchema 子类在视图中查找 schema_extra_info。这是可以的(实际上并没有什么害处),但这意味着你的架构逻辑最终会分散在许多不同的地方。

相反,尝试对 AutoSchema 进行子类化,以便 extra_info 不会泄漏到视图中

class BaseSchema(AutoSchema):
    """
    AutoSchema subclass that knows how to use extra_info.
    """

    ...


class CustomSchema(BaseSchema):
    extra_info = ...  # some extra info


class CustomView(APIView):
    schema = CustomSchema()

这种风格稍微冗长一些,但保持了与架构相关的代码的封装。在术语中,它更内聚。它将使你的其他 API 代码更加简洁。

如果某个选项适用于多个视图类,而不是为每个视图创建特定的子类,你可能会发现允许将该选项指定为基本 AutoSchema 子类的 __init__() 关键字参数更方便

class CustomSchema(BaseSchema):
    def __init__(self, **kwargs):
        # store extra_info for later
        self.extra_info = kwargs.pop("extra_info")
        super().__init__(**kwargs)


class CustomView(APIView):
    schema = CustomSchema(extra_info=...)  # some extra info

这样可以节省你为常用选项为每个视图创建自定义子类的麻烦。

并非所有 AutoSchema 方法都公开相关的 __init__() kwargs,但对于更常用的选项来说,它们确实公开。

AutoSchema 方法

get_components()

生成描述请求和响应正文的 OpenAPI 组件,从序列化器派生其属性。

返回将组件名称映射到生成表示形式的字典。默认情况下,它只包含一对,但你可以覆盖 get_components() 以返回多对,如果你的视图使用多个序列化器。

get_component_name()

从序列化器计算组件的名称。

如果你的 API 具有重复的组件名称,你可能会看到警告。如果是这样,你可以覆盖 get_component_name() 或传递 component_name __init__() kwarg(见下文)以提供不同的名称。

get_reference()

返回对序列化器组件的引用。如果你覆盖 get_schema(),这可能很有用。

map_serializer()

将序列化器映射到其 OpenAPI 表示形式。

大多数序列化器都应该符合标准的 OpenAPI object 类型,但你可能希望覆盖 map_serializer() 以自定义此字段或其他序列化器级别的字段。

map_field()

将各个序列化器字段映射到其架构表示形式。基本实现将处理 Django REST Framework 提供的默认字段。

对于 SerializerMethodField 实例,其架构未知,或者自定义字段子类,你应该覆盖 map_field() 以生成正确的架构

class CustomSchema(AutoSchema):
    """Extension of ``AutoSchema`` to add support for custom field schemas."""

    def map_field(self, field):
        # Handle SerializerMethodFields or custom fields here...
        # ...
        return super().map_field(field)

第三方包的作者应该旨在提供一个 AutoSchema 子类,以及一个 mixin,覆盖 map_field(),以便用户可以轻松地为其自定义字段生成架构。

get_tags()

OpenAPI 按标签对操作进行分组。默认情况下,标签取自路由 URL 的第一路径段。例如,像 /users/{id}/ 这样的 URL 将生成标签 users

你可以传递一个 __init__() kwarg 来手动指定标签(见下文),或覆盖 get_tags() 以提供自定义逻辑。

get_operation()

返回描述端点的 OpenAPI 操作对象,包括分页、过滤等路径和查询参数。

get_components() 一起,这是视图自省的主要入口点。

get_operation_id()

每个操作都必须有一个唯一的 operationid。默认情况下,operationId 从模型名称、序列化器名称或视图名称中推断出来。operationId 看起来像 "listItems"、"retrieveItem"、"updateItem" 等。按照惯例,operationId 是驼峰式大小写。

get_operation_id_base()

如果您有多个具有相同模型名称的视图,您可能会看到重复的 operationIds。

为了解决这个问题,您可以覆盖 `get_operation_id_base()` 来为 ID 的名称部分提供不同的基础。

get_serializer()

如果视图已实现 `get_serializer()`,则返回结果。

get_request_serializer()

默认情况下返回 `get_serializer()`,但可以覆盖它以区分请求和响应对象。

get_response_serializer()

默认情况下返回 `get_serializer()`,但可以覆盖它以区分请求和响应对象。

AutoSchema.__init__() kwargs

如果默认生成的数值不合适,AutoSchema 提供许多 `__init__()` kwargs,可用于常见自定义。

可用的 kwargs 是

  • tags:指定标签列表。
  • component_name:指定组件名称。
  • operation_id_base:指定操作 ID 的资源名称部分。

在视图中声明 `AutoSchema` 实例时,传递 kwargs

class PetDetailView(generics.RetrieveUpdateDestroyAPIView):
    schema = AutoSchema(
        tags=['Pets'],
        component_name='Pet',
        operation_id_base='Pet',
    )
    ...

假设一个 `Pet` 模型和 `PetSerializer` 序列化器,此示例中的 kwargs 可能不需要。但是,通常情况下,如果您有多个视图针对同一模型,或有多个视图具有相同名称的序列化器,则需要传递 kwargs。

如果您的视图具有经常需要的相关自定义,您可以为您的项目创建一个基础 `AutoSchema` 子类,它采用额外的 `__init__()` kwargs 来保存每个视图的 `AutoSchema` 子类。