Django restframework加Vue打造前后端分离的网站(十三)URL和Swagger中的自定义参数
在该系列的第九篇(查询筛选和搜索)中提到了筛选并返回部分符合条件的业务对象,要么放在url中http://127.0.0.1:8000/automation/api/computer/{type}/,要么是放在get请求的body中。此外,还有一种方式,类似一种筛选条件,http://127.0.0.1:8000/automation/api/computer/?type=tablet&year=2020,因为会涉及到swagger中的parameter的显示,这里就放在一起记录。
使用/?type=tablet&year=2020这种方式,不需要新加url路由,相当于传递{"type": "tablet", "year": "2020"}。如果我们需要对这两个参数做处理,就需要重写get_queryset这个方法。比如:
from .models import Computer
from .serializers import ComputerSerializer
from rest_framework import generics
from rest_framework import filters
class ComputerList(generics.ListCreateAPIView):
"""
get:
Return all computers.
post:
Create a new computer.
"""
serializer_class = ComputerSerializer
filter_backends = [filters.SearchFilter]
search_fields = ["=computer_type"]
def get_queryset(self):
queryset = Computer.objects.all().order_by("-id")
publish_year = self.request.query_params.get('year', None)
computer_type = self.request.query_params.get('type', None)
if publish_year:
queryset = queryset.filter(publish_year=publish_year)
if computer_type:
queryset = queryset.filter(computer_type=computer_type)
return queryset
此时便能接受处理year和type这两个url中的参数。并且返回符合条件的业务对象。如果url中有其他的不识别参数,这里也不会处理。
不过当我们现在打开swagger的docs页面时,却发现没有我们支持的这两个参数,只有我们用到的分页参数(page)和搜索参数(s)。如下图:
当我们点击"try it out"时,便只能尝试page和s。
对于目前支持的参数怎么办呢?要么是在doc中添加注释,类似:
"""
get:
Return all computers.
--
parameters:
type: get computer list by computer type
year: get computer list by publish year
post:
Create a new computer.
"""
swagger的显示便会变成这样,用来提示可以使用这两个参数。
不过这里只是起到了提示作用,不能在页面上直接尝试,需要在其他工具或者脚本中尝试这两个参数类别。
接下来就需要在swagger页面中添加自定义的参数,并可支持当前页面发请求。
此时在view.py中新建一个类:SimpleFilterBackend,并在ComputerList中的filter_backend指定。代码类似:
from .models import Computer
from .serializers import ComputerSerializer
from rest_framework import generics
from rest_framework import filters
from rest_framework.filters import BaseFilterBackend
from rest_framework.compat import coreapi
class SimpleFilterBackend(BaseFilterBackend):
def get_schema_fields(self, view):
return [coreapi.Field(
name='type',
location='query',
required=False,
type='string',
description= 'filter by computer type'
), coreapi.Field(
name='year',
location='query',
required=False,
type='string',
description= 'filter by publish year'
)]
def filter_queryset(self, request, queryset, view):
return queryset
class ComputerList(generics.ListCreateAPIView):
"""
get:
Return all computers.
--
parameters:
type: get computer list by computer type
year: get computer list by publish year
post:
Create a new computer.
"""
serializer_class = ComputerSerializer
filter_backends = [filters.SearchFilter, SimpleFilterBackend]
search_fields = ["=computer_type"]
def get_queryset(self):
queryset = Computer.objects.all().order_by("-id")
publish_year = self.request.query_params.get('year', None)
computer_type = self.request.query_params.get('type', None)
if publish_year:
queryset = queryset.filter(publish_year=publish_year)
if computer_type:
queryset = queryset.filter(computer_type=computer_type)
return queryset
此时swagger的页面就变成如下,并且可以支持当前页面尝试请求操作。
但是用这种方式有个问题,就是不同的view中涉及到的url参数名都可能不一样,如果用这种方式,那么每次都需要新定义一个SimpleFilterBackend并指定,不是太方便。
于是该方式可以更新为从doc中取值,动态的生成paramter。只是该方法会导致上面图片中的黄色部分的注释消失,因为要修改注释的格式。
先在utils中新增一个文件,并使其从doc中取出参数的信息。
from rest_framework.filters import BaseFilterBackend
from rest_framework.compat import coreapi
import yaml
class ParameterFilter(BaseFilterBackend):
def get_schema_fields(self, view):
default = {
"name": "no name",
"location": "query",
"required": False,
"type": "string",
"description": "no description"
}
params = yaml.load(view.__doc__)["parameters"]
fields = []
for p in params:
new_p = {}
for k in default.keys():
if k in p.keys():
new_p[k] = p[k]
else:
new_p[k] = default[k]
fields.append(coreapi.Field(
name=new_p["name"],
location=new_p["location"],
required=new_p["required"],
type=new_p["type"],
description=new_p["description"]
))
return fields
def filter_queryset(self, request, queryset, view):
return queryset
然后修改view.py中的注释信息和filter_backend:
from .models import Computer
from .serializers import ComputerSerializer
from rest_framework import generics
from rest_framework import filters
from utils.parameter_filter import ParameterFilter
class ComputerList(generics.ListCreateAPIView):
"""
get:
Return all computers.
parameters:
- name: type
description: filter by computer type
- name: year
description: filter by publish year
post:
Create a new computer.
"""
serializer_class = ComputerSerializer
filter_backends = [filters.SearchFilter, ParameterFilter]
search_fields = ["=computer_type"]
def get_queryset(self):
queryset = Computer.objects.all().order_by("-id")
publish_year = self.request.query_params.get('year', None)
computer_type = self.request.query_params.get('type', None)
if publish_year:
queryset = queryset.filter(publish_year=publish_year)
if computer_type:
queryset = queryset.filter(computer_type=computer_type)
return queryset
此时在swagger界面中就变成这个样子:
参考文章:https://github.com/marcgibbons/django-rest-swagger/issues/604