一、概述
drf-spectacular 是一个为 Django REST Framework (DRF) 设计的 OpenAPI 3.0 和 3.1 规范的生成器。它旨在提供既理智又灵活的方式来创建 API 文档,主要实现以下三个目标:
从 DRF 中提取尽可能多的 schema 信息
提供灵活性,使 schema 在现实世界中可用(不仅仅是示例)
生成一个与最流行的客户端生成器兼容的 schema
官网:
https://drf-spectacular.readthedocs.io/en/latest/
先来看一个效果图吧
用户详情
用户更新
接下来会详细介绍,如何实现
二、安装
环境说明
python:3.12.3
django:5.0.7
djangorestframework:3.15.2
drf-spectacular:0.28.0
三、配置
修改django项目中的settings.py
注册app
INSTALLED_APPS = ['django.contrib.admin','django.contrib.auth','django.contrib.contenttypes','django.contrib.sessions','django.contrib.messages','django.contrib.staticfiles','myapp','drf_spectacular', # 接口文档 swagger'drf_spectacular_sidecar', # 接口文档 swagger-ui
]
将 AutoSchema 注册到 DRF 中
最后一行添加
REST_FRAMEWORK = {# YOUR SETTINGS'DEFAULT_SCHEMA_CLASS': 'drf_spectacular.openapi.AutoSchema',
}
修改一些默认配置
最后一行添加
# drf-spectacular 配置
SPECTACULAR_SETTINGS = {'TITLE': '平台的API','DESCRIPTION': 'Your project description','VERSION': '1.0.0','SERVE_INCLUDE_SCHEMA': False,# OTHER SETTINGS'SWAGGER_UI_DIST': 'SIDECAR', # shorthand to use the sidecar instead'SWAGGER_UI_FAVICON_HREF': 'SIDECAR','REDOC_DIST': 'SIDECAR',
}
添加路由
在项目主目录的url中添加
from django.urls import pathfrom rest_framework.routers import DefaultRouter
from drf_spectacular.views import SpectacularAPIView, SpectacularRedocView, SpectacularSwaggerViewurlpatterns = [# YOUR PATTERNSpath('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的路由
]
四、视图函数配置
完整视图函数如下:
from django.shortcuts import render
from rest_framework.response import Response
from rest_framework import viewsets
from rest_framework import status
from drf_spectacular.utils import extend_schema, OpenApiParameter
from drf_spectacular.types import OpenApiTypes
from django.http import JsonResponse
from django.db import transactionfrom .modelsSerializers import *
from myapp.models import *
import json@extend_schema(tags=["用户管理"])
class UserView(viewsets.ViewSet):@staticmethoddef get_object(pk):"""获取用户对象:param self::param pk::return:"""try:obj = User.objects.get(pk=pk)return User.objects.get(pk=pk)except User.DoesNotExist:return None@extend_schema(operation_id="user-get", # 设置右上角的名称,需要唯一性summary="用户列表", # 接口上的备注 )def get(self, request, *args, **kwargs):"""获取所有用户"""queryset = User.objects.all()serializer = UserSerializer(instance=queryset, many=True)return Response(data=serializer.data, status=status.HTTP_200_OK)@extend_schema(operation_id="user-post", # 设置右上角的名称,需要唯一性summary="用户详情", # 接口上的备注 )def post(self, request, pk, *args, **kwargs):"""获取单个用户详细信息"""obj = self.get_object(pk=pk)if obj is None:return Response({"message": "user not found"}, status=status.HTTP_404_NOT_FOUND)serializer = UserSerializer(instance=obj)return Response(data=serializer.data, status=status.HTTP_200_OK)@extend_schema(operation_id="user-put", # 设置右上角的名称,需要唯一性summary="用户更新", # 接口上的备注# 执行序列化器responses=UserSerializer(many=True),# 对参数的修改parameters=[# 这是其中一个参数OpenApiParameter(# 参数的名称是donename="username",# 对参数的备注description="姓名",# 指定参数的类型type=OpenApiTypes.STR,# 指定必须给required=True,# 指定枚举项# enum = [True, False],),OpenApiParameter(name="account", description="账号", type=OpenApiTypes.STR, required=True),OpenApiParameter(name="is_superuser",description="是否是超级管理员1-是;2-否",type=OpenApiTypes.STR,required=True,enum=[1, 2],),OpenApiParameter(name="is_active",description="是否活动状态1-是;2-否",type=OpenApiTypes.STR,required=True,enum=[1, 2],),OpenApiParameter(name="phone", description="手机号", type=OpenApiTypes.STR, required=True),OpenApiParameter(name="email", description="邮箱", type=OpenApiTypes.STR, required=True),OpenApiParameter(name="role",description="角色1-管理员,2-普通用户",type=OpenApiTypes.STR,required=True,enum=[1, 2],),],)def put(self, request, pk):"""更新单个用户"""obj = self.get_object(pk=pk)if obj is None:return Response({"message": "user not found"}, status=status.HTTP_404_NOT_FOUND)# 获取请求参数account = request.data.get("account")email = request.data.get("email")is_active = request.data.get("is_active")is_superuser = request.data.get("is_superuser")phone = request.data.get("phone")role = request.data.get("role")username = request.data.get("username")try:with transaction.atomic(): # 使用事务# 修改用户信息ret = User.objects.filter(pk=pk).update(account=account,email=email,is_active=is_active,is_superuser=is_superuser,phone=phone,role=role,username=username,)if not ret:return JsonResponse({"status": status.HTTP_500_INTERNAL_SERVER_ERROR,"data": [],"msg": "修改用户失败",},status=status.HTTP_500_INTERNAL_SERVER_ERROR,)return JsonResponse({"status": status.HTTP_200_OK, "data": []},status=status.HTTP_200_OK,)except Exception as e:print(e)return JsonResponse({"status": status.HTTP_500_INTERNAL_SERVER_ERROR,"data": [],"msg": f"{e}",},status=status.HTTP_500_INTERNAL_SERVER_ERROR,)@extend_schema(operation_id="user-delete", # 设置右上角的名称,需要唯一性summary="用户删除", # 接口上的备注 )def delete(self, request, pk, *args, **kwargs):"""更新单个用户"""obj = self.get_object(pk=pk)if obj is None:return Response({"message": "user not found"}, status=status.HTTP_404_NOT_FOUND)obj.delete()return Response(status=status.HTTP_200_OK)
代码说明:
@extend_schema(tags=["用户管理"]) 定义标签名,显示效果就是这里
def get(self, request, *args, **kwargs):"""获取所有用户"""
注意看,3个引号部分,就是定义接口注释的,显示效果就是这里
@extend_schema(
operation_id="user-get", # 设置右上角的名称,需要唯一性summary="用户列表", # 接口上的备注
)
注意看,summary,就是定义接口描述的,显示效果就是这里
@extend_schema(operation_id="user-put", # 设置右上角的名称,需要唯一性summary="用户更新", # 接口上的备注# 执行序列化器responses=UserSerializer(many=True),# 对参数的修改parameters=[# 这是其中一个参数OpenApiParameter(# 参数的名称是donename="username",# 对参数的备注description="姓名",# 指定参数的类型type=OpenApiTypes.STR,# 指定必须给required=True,# 指定枚举项# enum = [True, False],),OpenApiParameter(name="account", description="账号", type=OpenApiTypes.STR, required=True),OpenApiParameter(name="is_superuser",description="是否是超级管理员1-是;2-否",type=OpenApiTypes.STR,required=True,enum=[1, 2],),OpenApiParameter(name="is_active",description="是否活动状态1-是;2-否",type=OpenApiTypes.STR,required=True,enum=[1, 2],),OpenApiParameter(name="phone", description="手机号", type=OpenApiTypes.STR, required=True),OpenApiParameter(name="email", description="邮箱", type=OpenApiTypes.STR, required=True),OpenApiParameter(name="role",description="角色1-管理员,2-普通用户",type=OpenApiTypes.STR,required=True,enum=[1, 2],),],)
extend_schema,这个装饰器主要用于修改view在文档中的定义,参数意义如下:
-
operation_id
:一个唯一标识ID,如果前端是使用这个接口文档生成的代码,那么这个参数将非常重要 -
parameters
:添加到列表中的附加或替换参数去自动发现字段。 -
responses
:修改序列化器。需要各种各样的可单独使用或组合使用的输入(有以下7种)- Serializer类 比如:
Serializer
- 序列化实例,比如:
Serializer(many=True)
- OpenApiTypes的基本类型或者实例 比如:
OpenApiTypes.BOOL
- OpenApiResponse类 例子见下面的备注
- PolymorphicProxySerializer类
- 1个字典,以状态码作为键, 以上其中一项作为值(是最常用的,格式
{200, None}
) - 1个字典,以状态码作为键,以media_type作为值 例子见下面的备注
- Serializer类 比如:
-
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
:规范扩展
这里有一个问题,始终无法解决,就是你在swagger文档页面,调试某些接口,点击Execute,请求参数都是在url里面的,无法在body里面显示。
例如,我尝试调用修改用户接口
这里会出现500错误,提示没有username参数。因为视图函数,接收参数是从body中获取的,不是从url中获取的,所以会找不到。
但是如果使用postman调用,是没有任何问题的。
因为在extend_schema中,定义的参数,默认类型就是OpenApiParameter.QUERY,例如:
# 这是其中一个参数OpenApiParameter(# 参数的名称是donename="username",# 对参数的备注description="姓名",# 指定参数的类型type=OpenApiTypes.STR,# 指定必须给required=True,# 指定枚举项# enum = [True, False],# 参数类型location=OpenApiParameter.QUERY,),
当指定OpenApiParameter.QUERY作为参数的位置属性时,这表示该参数将在 HTTP 请求的查询字符串中传递。例如,在一个GET请求中,查询字符串是在 URL 中?之后的部分。
我用chatget搜索了一下,location只有以下几种类型
1. QUERY
表示参数位于查询字符串(Query String)中。查询字符串是在 URL 中?之后的部分,通常用于GET请求传递参数。例如,在https://example.com/api/resource?param1 = value1¶m2 = value2中,param1和param2的位置就是QUERY。
2. PATH
用于表示参数是路径参数(Path Parameter)。路径参数是 URL 路径的一部分,用于识别特定的资源。例如,在https://example.com/api/users/{user_id}中,{user_id}就是路径参数,它的位置属性应该设置为PATH。
3. HEADER
表示参数位于 HTTP 请求头(Header)中。请求头包含了关于请求的元数据,如Authorization(用于认证)、Content - Type(用于指定请求体的内容类型)等。
4. COOKIE
当参数位于 HTTP 请求的 Cookie 中时,使用这个位置属性。Cookie 是服务器发送给浏览器,浏览器在后续请求中回传给服务器的一小段信息,常用于用户会话管理等。
根据以上结果,没有body,所以这个问题,只有等官方更新升级才能解决。
总结一下,swagger生成的文档,只需要观看即可,某些接口不能进行直接在swagger里面调用接口。
所以调用接口,还是需要专业工具,比如:postman或者代码实现。
本文参考链接:https://www.cnblogs.com/guangdelw/p/18054429