1. 项目概述:DRF与drf-yasg的API文档定制需求
在Django REST framework(DRF)开发生态中,自动生成API文档是提升团队协作效率的关键环节。drf-yasg作为目前最主流的DRF文档生成工具,其默认提供的Swagger UI界面虽然美观,但实际项目中我们经常需要对其展示的数据结构和样式进行深度定制。比如:
- 需要隐藏某些敏感接口或字段
- 对参数示例进行业务语义化改造
- 调整文档分类排序逻辑
- 注入自定义的说明文案
最近在电商后台API项目中,我们就遇到了需要动态修改文档数据的场景:同一套接口需要根据不同的客户等级展示不同的字段说明。经过多种方案的对比测试,最终总结出以下可复用的解决方案。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 核心原理剖析:drf-yasg的工作机制
2.1 文档生成流程解析
drf-yasg的文档生成分为三个阶段:
- Schema收集阶段:通过
get_schema_view()扫描所有APIView类 - OpenAPI转换阶段:将Django的URL路由转换为OpenAPI 2.0/3.0规范
- UI渲染阶段:使用Swagger UI或ReDoc渲染最终HTML
python复制# 典型初始化代码示例
from drf_yasg import openapi
from drf_yasg.views import get_schema_view
schema_view = get_schema_view(
openapi.Info(
title="API文档",
default_version='v1',
description="项目接口文档",
),
public=True,
)
2.2 关键可扩展点
通过分析源码,我们发现以下可干预点:
swagger_auto_schema装饰器:控制单个接口的文档属性SchemaGenerator类:影响全局路由收集逻辑OpenAPISchemaGenerator类:决定OpenAPI规范的转换规则SwaggerUIRenderer类:管理前端页面渲染
3. 数据修改的五种实战方案
3.1 方案一:装饰器级修改(精准控制)
适用于单个接口的文档定制,通过@swagger_auto_schema实现:
python复制from drf_yasg.utils import swagger_auto_schema
class ProductViewSet(ModelViewSet):
@swagger_auto_schema(
operation_description="获取商品列表(VIP可见价格)",
manual_parameters=[
openapi.Parameter(
'vip_level',
openapi.IN_HEADER,
description="会员等级",
type=openapi.TYPE_INTEGER
)
],
responses={
200: openapi.Response('成功', ProductSerializer),
403: "权限不足"
}
)
def list(self, request):
# 实际业务逻辑
关键技巧:通过
operation_id参数可以覆盖默认生成的接口ID,这对前端代码生成非常有用
3.2 方案二:序列化器动态调整
通过继承SchemaGenerator实现全局字段控制:
python复制from drf_yasg.generators import OpenAPISchemaGenerator
class CustomSchemaGenerator(OpenAPISchemaGenerator):
def get_schema(self, request=None, public=False):
schema = super().get_schema(request, public)
# 动态修改schema
if request.user.is_staff:
schema['info']['description'] = "管理员专属文档"
else:
del schema['paths']["/api/v1/users/"]
return schema
# settings.py配置
SWAGGER_SETTINGS = {
'DEFAULT_GENERATOR_CLASS': 'path.to.CustomSchemaGenerator'
}
3.3 方案三:模板覆盖法(前端定制)
直接修改Swagger UI模板文件:
- 定位模板文件位置:
bash复制find /usr/local/lib/ -name 'swagger-ui.html'
- 创建项目模板目录:
python复制TEMPLATES = [
{
'DIRS': [os.path.join(BASE_DIR, 'templates/drf-yasg')],
}
]
- 覆写模板片段(示例添加自定义CSS):
html复制<!-- templates/drf-yasg/swagger-ui.html -->
{% extends 'drf-yasg/swagger-ui.html' %}
{% block extra_styles %}
<style>
.opblock-summary-path { font-weight: bold; }
.model-box { background: #f5f5f5; }
</style>
{% endblock %}
3.4 方案四:请求钩子干预
利用DRF的初始化钩子:
python复制from drf_yasg.app_settings import swagger_settings
def patch_swagger_settings():
original_get_swagger_view = swagger_settings.GET_SCHEMA_VIEW
def patched_get_schema_view(*args, **kwargs):
view = original_get_swagger_view(*args, **kwargs)
# 在此处修改view的类属性
view.__dict__.update({
'extra_actions': {'post': 'create'},
})
return view
swagger_settings.GET_SCHEMA_VIEW = patched_get_schema_view
# 在AppConfig.ready()中调用
3.5 方案五:OpenAPI后处理
对生成的spec字典进行最终处理:
python复制from drf_yasg.inspectors import SwaggerAutoSchema
class PostProcessSchema(SwaggerAutoSchema):
def get_operation(self, operation_keys):
operation = super().get_operation(operation_keys)
# 添加自定义扩展字段
operation['x-business-unit'] = "ecommerce"
return operation
# 配置使用
SWAGGER_SETTINGS = {
'DEFAULT_AUTO_SCHEMA_CLASS': 'path.to.PostProcessSchema'
}
4. 企业级实践案例
4.1 多租户文档隔离
在SaaS系统中,不同租户需要看到不同的API文档:
python复制class TenantAwareSchemaGenerator(OpenAPISchemaGenerator):
def get_schema(self, request=None, public=False):
schema = super().get_schema(request, public)
tenant = request.tenant
# 根据租户过滤接口
allowed_paths = get_tenant_permissions(tenant)
schema['paths'] = {
path: spec for path, spec in schema['paths'].items()
if path in allowed_paths
}
# 添加租户标记
schema['info']['x-tenant-id'] = tenant.id
return schema
4.2 动态字段说明
根据用户角色显示不同的字段说明:
python复制class DynamicFieldSchema(SwaggerAutoSchema):
def get_response_schemas(self, response_serializers):
responses = super().get_response_schemas(response_serializers)
if self.request.user.is_vip:
responses[200]['schema']['properties']['price'][
'description'] = "VIP专属折扣价"
else:
del responses[200]['schema']['properties']['price']
return responses
5. 性能优化与调试技巧
5.1 文档生成加速
大型项目文档生成可能很慢,推荐方案:
- 缓存机制:
python复制from django.core.cache import caches
class CachedSchemaGenerator(OpenAPISchemaGenerator):
cache_key = 'api_schema_v2'
def get_schema(self, request=None, public=False):
if not request and not public:
return caches['default'].get(self.cache_key)
schema = super().get_schema(request, public)
if not request and not public:
caches['default'].set(self.cache_key, schema, 3600)
return schema
- 懒加载策略:
python复制SWAGGER_SETTINGS = {
'DEFAULT_INFO': 'import_string("path.to.info")', # 延迟导入
'LAZY_SCHEMA': True # 启用懒加载
}
5.2 常见问题排查
-
文档不更新:
- 检查
@swagger_auto_schema的auto_schema参数是否被覆盖 - 清理浏览器缓存或使用
?format=openapi直接查看原始JSON
- 检查
-
字段缺失:
- 确认序列化器的
Meta.fields配置正确 - 检查
required=False的字段是否被过滤
- 确认序列化器的
-
性能问题:
- 使用
--nothreading启动开发服务器测试 - 通过
DEBUG_PROPAGATE_EXCEPTIONS=True定位异常
- 使用
6. 安全加固方案
6.1 生产环境配置
python复制SWAGGER_SETTINGS = {
'SECURITY_DEFINITIONS': {
'Bearer': {
'type': 'apiKey',
'name': 'Authorization',
'in': 'header'
}
},
'LOGIN_URL': '/admin/login/',
'LOGOUT_URL': '/admin/logout/',
'VALIDATOR_URL': None, # 禁用在线校验
}
6.2 敏感信息过滤
python复制from drf_yasg import openapi
class SecurityFilter(SwaggerAutoSchema):
def get_operation(self, operation_keys):
operation = super().get_operation(operation_keys)
if 'password' in str(operation):
operation['responses'] = {
'400': openapi.Response('参数错误')
}
return operation
在实际项目中,我们最终采用了方案二和方案五的组合:通过自定义SchemaGenerator实现租户隔离,再结合OpenAPI后处理添加业务元数据。这种方案在保持性能的同时,满足了产品经理对文档展示的各种定制需求。一个特别实用的技巧是在get_schema方法中添加缓存逻辑,使文档生成时间从原来的8秒降低到300毫秒。
