前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >DRF Swagger自定义的action文档参数实现

DRF Swagger自定义的action文档参数实现

作者头像
用户1416054
发布2018-08-02 11:50:40
3.6K1
发布2018-08-02 11:50:40
举报
文章被收录于专栏:JackeyGao的博客

DRF Swagger自定义的action文档参数实现

Posted July 04, 2018

#Swagger

这里不讲 DRF(django rest framework)DRS(django rest swagger) 如何结合使用, 在以上两个项目文档中都有相关文档。

在安装完毕后, Swagger 可以自动通过我们锁定一的 serializer 来自动生成各个方法(GET, POST, PUT, DELETE)的coreapi.link(是一套 REST Docuemnt 描述工具), 这样后在Swagger上就可以根据相关的 Link 识别出所需的参数(Query或者Form)了。

但大多数我们往往需要根据特定的需求, 做一些自定义的接口, 比如使用的 api_view 装饰器定义的函数式视图, 或者使用DRF 中的 action 装饰器定义的自定义接口(在一些较早的DRF版本中为list_routedetail_route)。 那么这种情况下, 一些query和定义的 Form 不能直接在 swagger 中很好的展示出来,所以文档性描述接口语言, 在这个时候很是需要。

下面我们对一个接口进行改造, 改造前

form

Python

代码语言:javascript
复制
from django import forms


class RegisterForm(forms.Form):
    name = forms.CharField(label="name", required=True)

API VIEW

Python

代码语言:javascript
复制
@api_view(["GET", "POST"])
def register(request):
    if request.method == 'GET':
        form = RegisterForm(request.GET)
    else:
        form = RegisterForm(request.data)
    
    if not form.is_valid():
        raise_as_form(form)

    ip = request.META["REMOTE_HOST"]
    REGISTER_NODE_CACHE[ip] = form.data["name"]
    node = Node.objects.filter(ip=ip).first()

    if not node:
        raise ParseError("%s 没有创建节点" % ip)
    
    jobs = Job.objects.filter(src=node)

    serializer = JobSerializer(jobs, many=True)

    return Response(serializer.data)

默认情况下, swagger 不是显示出来这个接口会接受 name 字段.

DRF 的 schema 是控制接口参数架构的组件, 我们基于默认的 AutoSchema 重写一个新的 Schama.

主要逻辑为, 当为函数式视图或者为view 的 action的 endpoint 则通过 yaml 格式的文档描述,其他则通过默认的行为获取接口 link。

Python

代码语言:javascript
复制
# -*- coding: utf-8 -*-
import six, yaml

if six.PY3:
    from urllib.parse import urljoin
else:
    from urlparse import urljoin

from rest_framework.compat import coreapi
from rest_framework.schemas import SchemaGenerator
from rest_framework_swagger import renderers
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework import exceptions
from rest_framework.permissions import AllowAny
from rest_framework.schemas.generators import is_custom_action
from rest_framework.schemas.inspectors import AutoSchema


class CustomViewSchema(AutoSchema):

    def get_link(self, path, method, base_url):
        """
        __doc__ in yaml format, eg:
        desc: the desc of this api.
        parameters:
        - name: mobile
          desc: the mobile number
          type: string
          required: true
          location: query
        - name: promotion
          desc: the activity id
          type: int
          required: true
          location: form
        """
        if hasattr(self.view, 'action'):
            # Viewsets have explicitly named actions.
            action = self.view.action
        else:
            action = ''

        if not is_custom_action(action):
            # print(path, 'is api view')
            return super(CustomViewSchema, self).get_link(path, method, base_url)
            
        fields = self.get_path_fields(path, method)

        yaml_doc = None
        if self.view and self.view.__doc__:
            try:
                yaml_doc = yaml.load(self.view.__doc__)
            except:
                yaml_doc = None
        
        # print yaml_doc
        if yaml_doc and 'desc' in yaml_doc:
            desc = yaml_doc.get('desc', '')
            _method_desc = desc
            params = yaml_doc.get('parameters', [])
            for i in params:
                _name = i.get('name')
                _desc = i.get('desc')
                _required = i.get('required', True)
                _type = i.get('type', 'string')
                _location = i.get('location', 'query')
                f = coreapi.Field(
                    name=_name,
                    location=_location,
                    required=_required,
                    description=_desc,
                    type=_type
                )
                fields.append(f)
        else:
            _method_desc = self.view.__doc__ if self.view and self.view.__doc__ else ''
            fields += self.get_serializer_fields(path, method)
        fields += self.get_pagination_fields(path, method)
        fields += self.get_filter_fields(path, method)

        if fields and any([field.location in ('form', 'body') for field in fields]):
            encoding = self.get_encoding(path, method)
        else:
            encoding = None

        if base_url and path.startswith('/'):
            path = path[1:]

        return coreapi.Link(
            url=urljoin(base_url, path),
            action=method.lower(),
            encoding=encoding,
            fields=fields,
            description=_method_desc
        )

然后通过 schama 装饰器对 register 视图覆盖自定义的 AutoSchema 为 CustomViewSchema .

并加上 yaml 描述.

Python

代码语言:javascript
复制
@api_view(["GET", "POST"])
@schema(CustomViewSchema())
def register(request):
    """
    desc: 注册 agent 接口
    parameters:
    - name: name
      desc: The agent host name
      type: string
      required: true
      location: form
    """
    if request.method == 'GET':
        form = RegisterForm(request.GET)
    else:
        form = RegisterForm(request.data)
    
    if not form.is_valid():
        raise_as_form(form)

    ip = request.META["REMOTE_HOST"]
    REGISTER_NODE_CACHE[ip] = form.data["name"]
    node = Node.objects.filter(ip=ip).first()

    if not node:
        raise ParseError("%s 没有创建节点" % ip)
    
    jobs = Job.objects.filter(src=node)

    serializer = JobSerializer(jobs, many=True)

    return Response(serializer.data)

如果是基于 view 的 action 需要在 view 上定义 schema 属性.

Python

代码语言:javascript
复制
class XXXXXXViewSet(viewsets.ModelViewSet):
    schema = CustomViewSchema()

    @action(methods=['post'], detail=True, url_path='set-xy')
    def set_xy(self, request, pk=None):
        """
        desc: Set Node x y position.
        parameters:
        - name: x
          desc: The position of x.
          type: float
          required: true
          location: form
        - name: y
          desc: The position of y.
          type: float
          required: true
          location: form
        """
        obj = self.get_object()
        form = SetXYForm(request.data)

        if not form.is_valid():
            raise_as_form(form)

        ...

这时候, swagger UI 上执行的时候, 就可以显示出来参数的表单了.

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • DRF Swagger自定义的action文档参数实现
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档