如何在Django父类BaseView中使用drf-yasg生成Swagger文档?
解决drf-yasg父类装饰器无法识别子类属性的方案
下面提供几种无需逐个配置子类HTTP方法及装饰器的解决方案,核心思路是延迟获取子类属性,让swagger在生成文档时动态读取子类的serializer_class、model等配置:
方案1:利用Lambda延迟序列化器获取
直接在父类的@swagger_auto_schema中用lambda函数动态调用视图实例的序列化器方法,替代硬编码的序列化器类:
from drf_yasg.utils import swagger_auto_schema from rest_framework.generics import GenericAPIView class BaseView(GenericAPIView): # 确保子类重写此属性 serializer_class = None model = None @swagger_auto_schema( # POST/PUT请求动态获取请求体序列化器 request_body=lambda self: self.get_serializer_class(), # 响应动态获取序列化器 responses={200: lambda self: self.get_serializer_class()}, # 动态生成接口描述(可选) operation_description=lambda self: f"创建{self.model._meta.verbose_name}数据" ) def post(self, request, *args, **kwargs): # 通用POST逻辑 serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) serializer.save() return Response(serializer.data) @swagger_auto_schema( responses={200: lambda self: self.get_serializer_class(many=True)}, operation_description=lambda self: f"获取{self.model._meta.verbose_name_plural}列表" ) def get(self, request, *args, **kwargs): # 通用GET列表逻辑 queryset = self.get_queryset() serializer = self.get_serializer(queryset, many=True) return Response(serializer.data) # 子类仅需重写核心属性 class UserView(BaseView): serializer_class = UserSerializer model = User queryset = User.objects.all()
lambda函数会在swagger生成文档时执行,此时已经绑定了子类的实例,能正确拿到子类的序列化器和模型信息。
方案2:自定义类装饰器批量处理HTTP方法
如果父类包含多个HTTP方法(GET/POST/PUT/DELETE),可以写一个类装饰器,自动为所有方法注入动态swagger配置:
from drf_yasg.utils import swagger_auto_schema from rest_framework.generics import GenericAPIView def auto_swagger_base(cls): # 遍历需要处理的HTTP方法 for method_name in ['get', 'post', 'put', 'delete']: if not hasattr(cls, method_name): continue original_method = getattr(cls, method_name) # 根据请求方法动态配置swagger参数 swagger_kwargs = {} if method_name in ['post', 'put']: swagger_kwargs['request_body'] = lambda self: self.get_serializer_class() # 统一配置响应序列化器 swagger_kwargs['responses'] = { 200: lambda self: self.get_serializer_class(many=True if method_name == 'get' else False) } # 为原方法添加swagger装饰 decorated_method = swagger_auto_schema(**swagger_kwargs)(original_method) setattr(cls, method_name, decorated_method) return cls # 父类应用装饰器 @auto_swagger_base class BaseView(GenericAPIView): serializer_class = None model = None def get(self, request, *args, **kwargs): # 通用GET逻辑 pass def post(self, request, *args, **kwargs): # 通用POST逻辑 pass # 子类正常继承配置 class BookView(BaseView): serializer_class = BookSerializer model = Book queryset = Book.objects.all()
这个装饰器会自动遍历父类的HTTP方法,为每个方法添加带动态参数的swagger装饰,子类无需做额外配置。
方案3:重写swagger_auto_schema装饰器
自定义一个全局的swagger装饰器,默认自动读取当前视图的serializer_class,简化父类代码:
from drf_yasg.utils import swagger_auto_schema as original_swagger def swagger_auto_schema(**kwargs): # 自动注入动态请求体和响应配置 if 'request_body' not in kwargs: kwargs['request_body'] = lambda self: self.get_serializer_class() if 'responses' not in kwargs: kwargs['responses'] = {200: lambda self: self.get_serializer_class()} return original_swagger(**kwargs) # 父类使用自定义装饰器 class BaseView(GenericAPIView): serializer_class = None model = None @swagger_auto_schema(operation_description="创建资源") def post(self, request, *args, **kwargs): pass @swagger_auto_schema(operation_description="获取资源列表") def get(self, request, *args, **kwargs): pass # 子类配置属性即可 class OrderView(BaseView): serializer_class = OrderSerializer model = Order queryset = Order.objects.all()
这种方式下,父类的装饰器会自动处理序列化器的动态获取,子类只需要专注于业务属性的配置。
内容的提问来源于stack exchange,提问作者joseph Kalash
相关产品推荐
相关产品推荐

