首页 > 其他分享 >Django中的Swagger文档库--drf-spectacular

Django中的Swagger文档库--drf-spectacular

时间:2024-03-05 17:46:08浏览次数:27  
标签:description spectacular -- Todo Django import drf type schema

在用django开发后端接口的时候,通常会面临写接口文档的问题,尤其项目大了之后,写文档就更加头疼了,如果能够在写完代码后,自动生成接口文档,那该多好啊

所以,咱们这里要介绍一个比较厉害的库 drf-spectacular

这个库能够自动生成OpenApi 3.0 的接口文档,并给出目前比较流行的swagger UI的界面

这个库主要实现了3个目标

  • 从DRF中提取更多的schema信息
  • 提供灵活性,使schema在现实世界中可用(不仅仅是示例)
  • 生成一个与最流行的客户端生成器配合良好的schema

官网

https://drf-spectacular.readthedocs.io/en/latest/

环境

python版本 django版本 DRF版本 drf-spectacular版本
3.10.4 3.2.23 3.14.0 0.27.1

安装

pip install drf-spectacular

Django项目中的配置

settings.py 文件中的配置

注册app

INSTALLED_APPS = [
    # ALL YOUR APPS
    'drf_spectacular',
]

AutoSchema 注册到 DRF

REST_FRAMEWORK = {
    # YOUR SETTINGS
    'DEFAULT_SCHEMA_CLASS': 'drf_spectacular.openapi.AutoSchema',
}

修改一些默认配置

SPECTACULAR_SETTINGS = {
    'TITLE': '平台的API',
    'DESCRIPTION': '这是项目的API文档',
    'VERSION': '3.0.0',
    'SERVE_INCLUDE_SCHEMA': False,
    'SCHEMA_PATH_PREFIX': None,
    # 或者如果有统一的前缀,可以设置成
    # 'SCHEMA_PATH_PREFIX': '^/api/',
    "SWAGGER_UI_SETTINGS": {
        "deepLinking": True,
        "persistAuthorization": True,
        "displayOperationId": True,
    },
    # 修改图标
    "SWAGGER_UI_FAVICON_HREF": "https://xxxxx/xxxx/xxx/20231102152526.png",
}

添加路由

在项目主目录的url中添加

from django.contrib import admin
from django.urls import path, include
from rest_framework_simplejwt.views import (
    TokenRefreshView,
)

urlpatterns = [
    # YOUR PATTERNS
    path('doc/schema/', SpectacularAPIView.as_view(), name='schema'), # schema的配置文件的路由,下面两个ui也是根据这个配置文件来生成的
    path('doc/swagger/', SpectacularSwaggerView.as_view(url_name='schema'), name='swagger-ui'), # swagger-ui的路由
    path('doc/redoc/', SpectacularRedocView.as_view(url_name='schema'), name='redoc'), # redoc的路由
]

测试

按照上面的配置,就已经可以正常获取接口文档页面了

第一个schema配置文件的文档

访问 http://127.0.0.1:8000/doc/schema/

可以得到下面的yaml配置文件

openapi: 3.0.3
info:
  title: Todo API
  version: 3.0.0
  description: 这是项目的API文档
paths:
  /todo/:
    get:
      operationId: todo_list
      parameters:
      - in: query
        name: done
        schema:
          type: boolean
      tags:
      - todo
      security:
      - cookieAuth: []
      - basicAuth: []
      - {}
      responses:
        '200':
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Todo'
          description: ''
    post:
      operationId: todo_create
      tags:
      - todo
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Todo'
          application/x-www-form-urlencoded:
            schema:
              $ref: '#/components/schemas/Todo'
          multipart/form-data:
            schema:
              $ref: '#/components/schemas/Todo'
        required: true
      security:
      - cookieAuth: []
      - basicAuth: []
      - {}
      responses:
        '201':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Todo'
          description: ''
  /todo/{id}/:
    get:
      operationId: todo_retrieve
      parameters:
      - in: path
        name: id
        schema:
          type: integer
        description: A unique integer value identifying this 待办项.
        required: true
      tags:
      - todo
      security:
      - cookieAuth: []
      - basicAuth: []
      - {}
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Todo'
          description: ''
    put:
      operationId: todo_update
      parameters:
      - in: path
        name: id
        schema:
          type: integer
        description: A unique integer value identifying this 待办项.
        required: true
      tags:
      - todo
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Todo'
          application/x-www-form-urlencoded:
            schema:
              $ref: '#/components/schemas/Todo'
          multipart/form-data:
            schema:
              $ref: '#/components/schemas/Todo'
        required: true
      security:
      - cookieAuth: []
      - basicAuth: []
      - {}
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Todo'
          description: ''
    patch:
      operationId: todo_partial_update
      parameters:
      - in: path
        name: id
        schema:
          type: integer
        description: A unique integer value identifying this 待办项.
        required: true
      tags:
      - todo
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PatchedTodo'
          application/x-www-form-urlencoded:
            schema:
              $ref: '#/components/schemas/PatchedTodo'
          multipart/form-data:
            schema:
              $ref: '#/components/schemas/PatchedTodo'
      security:
      - cookieAuth: []
      - basicAuth: []
      - {}
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Todo'
          description: ''
    delete:
      operationId: todo_destroy
      parameters:
      - in: path
        name: id
        schema:
          type: integer
        description: A unique integer value identifying this 待办项.
        required: true
      tags:
      - todo
      security:
      - cookieAuth: []
      - basicAuth: []
      - {}
      responses:
        '204':
          description: No response body
components:
  schemas:
    PatchedTodo:
      type: object
      properties:
        id:
          type: integer
          readOnly: true
        content:
          type: string
          title: 待办内容
          maxLength: 200
        done:
          type: boolean
          title: 是否完成
    Todo:
      type: object
      properties:
        id:
          type: integer
          readOnly: true
        content:
          type: string
          title: 待办内容
          maxLength: 200
        done:
          type: boolean
          title: 是否完成
      required:
      - content
      - id
  securitySchemes:
    basicAuth:
      type: http
      scheme: basic
    cookieAuth:
      type: apiKey
      in: cookie
      name: sessionid

访问第二个 swagger UI 的页面

访问 http://127.0.0.1:8000/doc/swagger/

里面的各个接口也都是可以在本页面进行测试的

访问第三个 redoc 的页面

访问 http://127.0.0.1:8000/doc/redoc/


该页面相对于 swagger UI 的页面的缺陷可能是无法测试,只能查看,无法测试

自定义界面各项信息

修改备注

一般自动生成的页面是这样的

就没有任何的备注描述

当在序列化器上添加注释时

from rest_framework import serializers
from .models import Todo


class TodoSerializer(serializers.ModelSerializer):
    """序列化器中的注释"""
    class Meta:
        model = Todo
        fields = '__all__'

页面的变化

当在视图集上添加注释

from rest_framework.viewsets import ModelViewSet
from .models import Todo
from .serializers import TodoSerializer


class TodoViewSet(ModelViewSet):
    """视图集中的注释"""
    queryset = Todo.objects.all()
    serializer_class = TodoSerializer

页面的变化

修改参数部分

当我们希望在接口页面能够详细的显示需要的参数信息的时候,就需要在对应的视图函数上做一定的修改

修改前

可以看到,右上角的接口名称是自动生成的,虽然也具有一定的辨识性,但是有的时候还是需要自定义

然后参数部分也没有任何注释信息

这个时候就需要用到一个装饰器 extend_schema

这个装饰器就可以修改接口上的信息

from rest_framework.viewsets import ModelViewSet
from .models import Todo
from .serializers import TodoSerializer
from django_filters.rest_framework import DjangoFilterBackend
from drf_spectacular.utils import extend_schema, OpenApiParameter
from drf_spectacular.types import OpenApiTypes


class TodoViewSet(ModelViewSet):
    """视图集中的注释"""
    queryset = Todo.objects.all()
    serializer_class = TodoSerializer

    filter_backends = [
        DjangoFilterBackend,
    ]
    filterset_fields = [
        "done"
    ]

    @extend_schema(
        operation_id = "TodoLists", # 设置右上角的名称,需要唯一性
        summary = "待办项列表",  # 接口上的备注
        # 执行序列化器
        responses = TodoSerializer(many = True),
        # 对参数的修改
        parameters = [
            # 这是其中一个参数
            OpenApiParameter(
                # 参数的名称是done
                name = "done",
                # 对参数的备注
                description = "是否完成",
                # 指定参数的类型
                type = OpenApiTypes.BOOL,
                # 指定必须给
                required = True,
                # 指定枚举项
                enum = [True, False],
            )
        ])
    # 因为原来是视图集中自动生成的视图函数,如果希望修改,就需要重写,这里直接甩给父类处理
    def list(self, request, *args, **kwargs):
        return super().list(request, *args, **kwargs)

修改后的页面

extend_schema 装饰器的介绍

源码函数签名

导入 from drf_spectacular.utils import extend_schema

def extend_schema(
        operation_id: Optional[str] = None,
        parameters: Optional[Sequence[Union[OpenApiParameter, _SerializerType]]] = None,
        request: Any = empty,
        responses: Any = empty,
        auth: Optional[Sequence[str]] = None,
        description: Optional[_StrOrPromise] = None,
        summary: Optional[_StrOrPromise] = None,
        deprecated: Optional[bool] = None,
        tags: Optional[Sequence[str]] = None,
        filters: Optional[bool] = None,
        exclude: Optional[bool] = None,
        operation: Optional[_SchemaType] = None,
        methods: Optional[Sequence[str]] = None,
        versions: Optional[Sequence[str]] = None,
        examples: Optional[Sequence[OpenApiExample]] = None,
        extensions: Optional[Dict[str, Any]] = None,
        callbacks: Optional[Sequence[OpenApiCallback]] = None,
        external_docs: Optional[Union[Dict[str, str], str]] = None,
) -> Callable[[F], F]:

参数解释

这个装饰器主要用于修改view在文档中的定义,参数意义如下:

  • operation_id:一个唯一标识ID,如果前端是使用这个接口文档生成的代码,那么这个参数将非常重要

  • parameters:添加到列表中的附加或替换参数去自动发现字段。

  • responses:修改序列化器。需要各种各样的可单独使用或组合使用的输入(有以下7种)

    • Serializer类 比如:Serializer
    • 序列化实例,比如:Serializer(many=True)
    • OpenApiTypes的基本类型或者实例 比如:OpenApiTypes.BOOL
    • OpenApiResponse类 例子见下面的备注
    • PolymorphicProxySerializer类
    • 1个字典,以状态码作为键, 以上其中一项作为值(是最常用的,格式 {200, None})
    • 1个字典,以状态码作为键,以media_type作为值 例子见下面的备注
  • request:替换序列化,接受各种输入

    • Serializer 类或者实例
    • OpenApiTypes基本类型或者实例
    • PolymorphicProxySerializer类
    • 1个字典,以media_type作为键,以上其中一项作为值
  • auth:用auth方法的显式列表替换发现的auth

  • description:替换发现的文档字符串

  • summary:一个可选的短的总结描述

  • deprecated:将操作标记为已弃用

  • tags:覆盖默认标记列表

  • exclude:设置为True以从schema中排除操作

  • operation:手动覆盖自动发现将生成的内容。你必须提供一个兼容OpenAPI3的字典,该字典可以直接翻译成YAML。

  • methods:检查extend_schema中特殊的方法,默认匹配所有

  • versions:检查extend_schema中特殊的API版本,默认匹配所有

  • example:将请求/响应示例附加到操作中

  • extensions:规范扩展

备注:

OpenApiResponse类
OpenApiResponse 就是用来封装一个具体的 HTTP 响应预期,包括其状态码(status code)、响应体的内容类型(content type)以及响应体所遵循的 JSON Schema 或其他数据模型。
例如:

from drf_spectacular.utils import extend_schema, OpenApiResponse

error_response = OpenApiResponse(
    description="An error occurred",
    content={"application/json": {"schema": ErrorSchema}},
    status_code=status.HTTP_400_BAD_REQUEST,
)

@extend_schema(responses={200: ItemSchema(), **{status.HTTP_400_BAD_REQUEST: error_response}})
def my_view(request):
    ...

上面的 “以状态码作为键,以media_type作为值” 的形式

{
   200: {"description": "成功", "content": {"application/json": {"schema": MySuccessResponseSchema}}},
   400: {"description": "错误请求", "content": {"application/json": {"schema": MyErrorResponseSchema}}},
})

其中的media_type 就是指的类似 "application/json"

例子

from drf_spectacular.utils import extend_schema

class LoginView(GenericAPIView):
    ......

    @extend_schema(
        tags=['Common'],
        summary='Login',
        description='登录接口',
        responses={200: str, 401: str}
    )
    def post(self, request: Request):
        pass
        

class RegisterView(GenericAPIView):
    ......

    @extend_schema(
        tags=['Common'],
        summary='Register',
        description='注册接口',
        responses={201: UserInfoSerializer, 400: str}
    )
    def post(self, request: Request):
        pass

class TodoViewSet(ModelViewSet):
    ......

    @extend_schema(
        operation_id = "TodoLists", # 设置右上角的名称,需要唯一性
        summary = "待办项列表",  # 接口上的备注
        # 执行序列化器
        responses = TodoSerializer(many = True),
        # 对参数的修改
        parameters = [
            # 这是其中一个参数
            OpenApiParameter(
                # 参数的名称是done
                name = "done",
                # 对参数的备注
                description = "是否完成",
                # 指定参数的类型
                type = OpenApiTypes.BOOL,
                # 指定必须给
                required = True,
                # 指定枚举项
                enum = [True, False],
            )
        ])
    def list(self, request, *args, **kwargs):
        pass

自定义认证方式

在项目中我们使用了JWT作为登录认证,而 drf-spectacular 只对 SessionBasicToken 做了适配

rest_framework.authentication.SessionAuthentication
rest_framework.authentication.BasicAuthentication
rest_framework.authentication.TokenAuthentication

这个我们在 drf-spectacular/authentication.py 文件中可以看到,这个的作用就是在文档中显示什么样认证页面

默认页面

对于认证页面的显示,主要是根据 settings.py 配置中的

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework.authentication.BasicAuthentication',
        'utils.auth.authentication.JwtAuthentication'
    ],
    ......
}

如果 drf-spectacular 可以识别 DEFAULT_AUTHENTICATION_CLASSES 下的认证方式,就会在文档登录页面上显示对应的认证方式,这里我们有自定义的认证方式,如果需要显示,要做一下适配:

from drf_spectacular.extensions import OpenApiAuthenticationExtension
from drf_spectacular.plumbing import build_bearer_security_scheme_object


class JWTTokenScheme(OpenApiAuthenticationExtension):
    target_class = 'utils.auth.authentication.JwtAuthentication'
    name = 'JwtTokenAuth'
    match_subclasses = True
    priority = 1

    def get_security_definition(self, auto_schema):
        return build_bearer_security_scheme_object(
            header_name='Authorization',
            token_prefix=self.target.keyword,
            bearer_format='JWT'
        )

简单解释一下,首先要继承 OpenApiAuthenticationExtension,然后target_class中要写我们在 DEFAULT_AUTHENTICATION_CLASSES 中配置的认证路径,然后重新 get_security_definition 函数,返回一个字典对象,字典的键可以在 https://spec.openapis.org/oas/v3.0.3#fixed-fields-22 找到

然后再看登录认证页面

因为我们在 DEFAULT_AUTHENTICATION_CLASSES 中配置了两种认证方式,因此页面就会显示两种认证方式

标签:description,spectacular,--,Todo,Django,import,drf,type,schema
From: https://www.cnblogs.com/guangdelw/p/18054429

相关文章

  • fastadmin 多选,单选,不选
    文档https://static.kancloud.cn/wangzhaozhen/fastadmin/2291938多选<divclass="form-group"><labelclass="control-labelcol-xs-12col-sm-4">{:__('拒绝原因')}:</label>......
  • vue中PDF文件转图片方式
    1、在vue中安装依赖 pdfjs-dist  2、在需要引用的文件中添加import*aspdfjsfrom'pdfjs-dist'import*aspdfjsWorkerfrom'pdfjs-dist/build/pdf.worker.entry'pdfjs.GlobalWorkerOptions.workerSrc=pdfjsWorker3、编写需要转换的方法<divv-for="(i......
  • 自定义Hooks:四个典型的使用场景
    一、如何用好hook要用好ReactHooks,很重要的一点,就是要能够从Hooks的角度去思考问题。要做到这一点其实也不难,就是在遇到一个功能开发的需求时,首先问自己一个问题:这个功能中的哪些逻辑可以抽出来成为独立的Hooks?这样问的目的,是为了让我们尽可能的吧业务陆奥及拆分......
  • React的7个常用Hooks及使用场景(含示例)
    React是一款非常流行的JavaScript库,它提供了许多Hooks,用于管理函数组件的状态和生命周期。下面是React的每个Hooks的使用场景和示例:No1、useStateuseState用于在函数组件中管理状态。它返回一个包含当前状态和一个更新状态的函数的数组。更新状态的函数可以接受一个新的值......
  • 09django
    作业#1.用户名动态校验<p>username:<inputtype="text"id="i1"><spanstyle="color:red"id="error"></span></p><script>//这个功能可以使用前端代码完成但是安全性不高最好还是经过后端(前端的校验是弱不禁风的!!!)......
  • Arcmap简化面操作.
     简化面操作能移除面要素中的曲线段环.使其在遍历要素JSON时,使用rings,(当有曲线换时,是  curveRings属性) 1.打开“简化面”工具.  (英文搜索:  SimplifyPolygon)     2.执行简化.>注意! 需要设置容差.容差设置精度值越小,越贴近原始......
  • Pokemon Go自動走路 iOS/Android 在家玩寶可夢不用出門 不用移動
    有時您可能想知道如何在不動的情況下玩PokemonGo。好消息是,我們將為您介紹PokemonGo自動步行,以偽造GPS位置。PokemonGo欺騙器將作為位置變換器引入。閱讀更多有關如何在PokemonGo中無需步行即可移動的方法。第1部分.是否可以不動地玩PokemonGO 在家玩寶可夢......
  • 肖SIR__数据库之子查询__12.4
    一、什么是子查询?一个查询嵌套另一个查询例如:selectdept1fromdeptwheredept_name="iT技术";#dept中"iT技术"的编号103selectsidfromempwheredept2=103#103=前面的语句selectsidfromempwheredept2=(selectdept1fromdeptwheredept_name="iT技术") 二......
  • 三月五日 课堂练习
    课堂练习01题目:计算最长英语单词链。一、题目内容:大家经常玩成语接龙游戏,我们试一试英语的接龙吧:一个文本文件中有N个不同的英语单词,我们能否写一个程序,快速找出最长的能首尾相连的英语单词链,每个单词最多只能用一次。最长的定义是:最多单词数量,和单词中字母的数量无关。二......
  • 自我介绍+软工五问
    自我介绍+软工五问这个作业属于哪个课程https://edu.cnblogs.com/campus/gdgy/SoftwareEngineering2024这个作业要求在哪里https://edu.cnblogs.com/campus/gdgy/SoftwareEngineering2024/homework/13135这个作业的目标学习与使用GitHub、Git、Markdown个人介......