Skip to content

DRF Extra Actions

冒菜略寡味 edited this page May 15, 2018 · 2 revisions

DRF 自定义路由行为

bills账目资源为例,我们需要一个批量删除接口,但DRF视图集中并未包含,这需要我们自定义一个额外接口;

文章marking-extra-actions-for-routing有介绍自定义接口的实现方式;

1. schema 结构

这里提及schema的原因在于,采用@action装饰器实现的接口,其在swagger文档中展示的请求参数是采用了serializer_class中设置的字段参数,而当我在实现一个批量删除功能的接口时,这些参数完全用不上,需要定义自己的请求参数;

那么如何自定义接口参数于swagger文档中呢?

这里在文档per-view-schema-customisation中提及了get_link(self, path, method, base_url)这个方法,它可以为视图接口指定请求参数;

# apps/bills/schemas.py
from rest_framework.schemas import AutoSchema
from coreapi import Field, Link, document
import coreschema

class BillSchema(AutoSchema):
  
  def get_link(self, path, method, base_url):
    link = super().get_link(path, method, base_url)
    # 无法指定bill_ids数组中的元素类型,这里元素定义为int但实际为str,无法处理...
    batch_del_fields = [
      Field(
        'bill_ids', 
        location='form', 
        required=True,
        schema=coreschema.Array(items=coreschema.Integer(),unique_items=True)
      )
    ]
    fields = link.fields
    if link.url == '/api/v1/bills/batch_del/':
      fields = tuple(batch_del_fields)
    
    link = Link(url=link.url, action=link.action, encoding=link.encoding, fields=fields, description=link.description)
    document.Link()
    return link

这里我们为Bills定义了一个schema,重写了get_link()该方法,方法内我们定义了一个名为bill_ids的数组类型请求参数,且规定在URL为:/api/v1/bills/batch_del/的时候设置为该自定义参数;

注:这里有个坑,schema=coreschema.Array(items=coreschema.Integer(),unique_items=True)这段定义的是一个内部元素为整形的数组,但实际在swagger中数组内部是str类型,暂无法解决,但没有太大影响;

然后,还需要再视图中指定schema:

# apps/bills/views.py
from apps.bills.schemas import BillSchema
...
class BillsViewSet(viewsets.ModelViewSet):
    serializer_class = BillSerializer
    ...
    schema = BillSchema() # 设置schema
    ...

2. serializer 序列化

schema处理完后,我们只是能处理了swagger文档的展示,而接口本身需要的请求入参验证,还需要自定义一个序列化器支持;

class BatchDelSerializer(serializers.Serializer):
    bill_ids = serializers.ListField(required=True, child=serializers.IntegerField())

我们定义了参数bill_ids,它是一个成员为整形的列表;

3. views 自定义接口实现

最后我们回到视图,实现批量删除接口:

# apps/bills/views.py
from rest_framework import viewsets, status
from rest_framework.decorators import action
from rest_framework.response import Response
from apps.bills.schemas import BillSchema
from apps.bills.serializers import BillSerializer, BatchDelSerializer
...
class BillsViewSet(viewsets.ModelViewSet):
    serializer_class = BillSerializer
    ...
    schema = BillSchema()
    ...

    # 批量删除
    @action(methods=['delete'], detail=False)
    def batch_del(self, request):
        """
        batch delete bills
        """
        serializer = BatchDelSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        bill_ids = serializer.data.get('bill_ids')
        bills_data = Bills.objects.filter(pk__in=bill_ids)
        bills_data.delete()
        return Response(status.HTTP_204_NO_CONTENT)

昨日擔當 昨日敢想

昨日轉眼 就跌撞

夏時夢長 秋時晝短

清冽途上 不遠望

Clone this wiki locally