第 14 章 · drf-spectacular 与 OpenAPI 文档
本章目标:安装配置 drf-spectacular;理解 SPECTACULAR_SETTINGS;使用 @extend_schema 文档化请求体、响应与错误码;暴露 Swagger UI 与 Redoc;预留 Bearer JWT 安全方案;与 Flask ch14 Flask-RESTX 对照,为 shop-demo /api/ 提供可交互契约。
学时建议:4~5 小时(含 2 小时文档站联调)
前置:本模块 ch01~ch13;重点复习 ch07 DRF 路由、ch13 ProductListSerializer / ProductWriteSerializer 字段定义。
14.1 为什么需要 API 文档
┌─────────────┐ OpenAPI 契约 ┌─────────────┐
│ user-demo │ ◄──────────────────► │ shop-demo │
│ 前端 SPA │ 路径/参数/响应模型 │ Django API │
└─────────────┘ └─────────────┘
└──────── Swagger UI 在线调试 ──────────┘
| 无文档 | 有 OpenAPI 文档 |
|---|---|
| 口头同步字段,易漂移 | 单一契约,版本可追踪 |
| 联调靠猜状态码 | 错误码与示例一目了然 |
| 新人上手慢 | /api/schema/swagger-ui/ 自助试调 |
| 前端 Mock 手写 | 从 schema 生成类型(选修) |
OpenAPI 3 核心:paths(路径与方法)、components/schemas(模型)、responses(状态码)、securitySchemes(Bearer,ch15 实装)。
示例域名:api.example.com;前端:user-demo;禁止写入真实生产密钥。
14.2 安装 drf-spectacular
cd ~/python-learn/shop-demo
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install "drf-spectacular>=0.27,<1"
pip freeze | grep spectacular >> requirements.txt
shopdemo/settings.py:
INSTALLED_APPS = [
# ...
"rest_framework",
"drf_spectacular",
"catalog",
]
REST_FRAMEWORK = {
# ... ch07 已有配置
"DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema",
}
SPECTACULAR_SETTINGS = {
"TITLE": "shop-demo API",
"DESCRIPTION": "教学项目 · 商品与订单 REST API(虚构 api.example.com 契约)",
"VERSION": "1.0.0",
"SERVE_INCLUDE_SCHEMA": False,
"COMPONENT_SPLIT_REQUEST": True, # 读写模型分离
"SCHEMA_PATH_PREFIX": r"/api",
"TAGS": [
{"name": "products", "description": "商品资源"},
{"name": "auth", "description": "认证(ch15 JWT)"},
],
"SECURITY": [{"BearerAuth": []}],
"APPEND_COMPONENTS": {
"securitySchemes": {
"BearerAuth": {
"type": "http",
"scheme": "bearer",
"bearerFormat": "JWT",
"description": "格式: Bearer <access_token>(ch15 SimpleJWT 签发)",
}
}
},
}
shopdemo/urls.py:
from django.contrib import admin
from django.urls import path, include
from drf_spectacular.views import (
SpectacularAPIView,
SpectacularSwaggerView,
SpectacularRedocView,
)
urlpatterns = [
path("admin/", admin.site.urls),
path("api/", include("catalog.urls")),
# OpenAPI 文档(开发期开启,生产见 14.8)
path("api/schema/", SpectacularAPIView.as_view(), name="schema"),
path("api/schema/swagger-ui/", SpectacularSwaggerView.as_view(url_name="schema"), name="swagger-ui"),
path("api/schema/redoc/", SpectacularRedocView.as_view(url_name="schema"), name="redoc"),
]
启动后访问:
| 路径 | 内容 |
|---|---|
/api/schema/swagger-ui/ | Swagger UI 交互试调 |
/api/schema/redoc/ | Redoc 只读文档 |
/api/schema/ | OpenAPI JSON/YAML |
14.3 自动生成与 Serializer 协同
drf-spectacular 默认从 Serializer、ViewSet、@action 推断 schema。ch13 的 Serializer 即文档单一真相源:
# catalog/serializers.py — 字段定义即 OpenAPI components
class ProductListSerializer(serializers.ModelSerializer):
category = CategorySerializer(read_only=True)
cover_thumb = serializers.SerializerMethodField()
class Meta:
model = Product
fields = ["id", "name", "slug", "price", "stock", "is_published", "category", "cover_thumb", "created_at"]
| 层级 | 职责 |
|---|---|
| DRF Serializer | 运行时校验与序列化 |
| AutoSchema | 从 Serializer 生成 components/schemas |
@extend_schema | 补充描述、示例、额外响应码 |
分页响应:PageNumberPagination 自动生成 count / next / previous / results 结构;若前端 user-demo 需固定契约,可在自定义 Pagination 类加 schema 属性或在 @extend_schema 中声明包装 Serializer。
14.4 @extend_schema 详解
# catalog/views.py(节选)
from drf_spectacular.utils import extend_schema, extend_schema_view, OpenApiParameter, OpenApiExample
@extend_schema_view(
list=extend_schema(
tags=["products"], summary="商品分页列表",
parameters=[
OpenApiParameter("search", str, description="按名称/slug 搜索"),
OpenApiParameter("page", int, description="页码"),
],
responses={200: ProductListSerializer(many=True)},
),
create=extend_schema(
tags=["products"], summary="创建商品",
request=ProductWriteSerializer,
responses={201: ProductDetailSerializer, 400: None, 401: None},
auth=["BearerAuth"],
),
)
class ProductViewSet(viewsets.ModelViewSet):
# get_serializer_class 同 ch13 ...