Django REST framework API Guide 之 Views

“御驾亲征 必须有两个条件
其一 万不得已
其二 征则必胜
天子乃国家的命脉
不到万不得已不可亲身赴险
天子一旦亲征
必须有绝对的胜算”
--- 引自《康熙王朝》台词

老大昨天去武汉了,非万不得已,看来是有绝对的胜算了。但愿瘟神快点走,工作生活节奏能恢复正常。

概览

在官网上,DRF 的 API Guide 涉及如下模块:

  • Requests 请求
  • Response 响应
  • Views 视图
  • Generic views 通用视图
  • Viewsets 视图集合类
  • Routers 路由器
  • Parsers 解释器
  • Renderers 渲染器
  • Serializers 序列化器
  • Serializer fields 序列化器字段
  • Serializer relations 序列化器之间的关系
  • Validators 验证器
  • Authentication 认证
  • Permissions 权限
  • Caching 缓存
  • throttling 限流
  • Filtering 过滤
  • Pagination 分页
  • Versioning 版本
  • Content negotiation 内容协商
  • Metadata 元数据
  • Schemas 架构
  • Format suffixes 格式后缀
  • Returning URLS 返回url
  • Exceptions 异常
  • Status codes 状态码
  • Testing 测试
  • Settings 设置

Views

“Django’s class-based views are a welcome departure from the old-style views.”
- Reinout van Rees

REST framework 提供了一个 APIView 类,他继承了 Django 的 View 类。
APIView 类和Django 标准的view 类在某些方面并不一致:

  • 进入相关handler 方法的Requests不是Django 的 HttpRequest 实例,而是 REST framework 的Request实例。
  • handler 方法可能返回 REST framework 的 Response,而不是 Django 的HttpResponse。APIView会处理内容协商,为Response设置正确的渲染器。
  • 任何 APIException 异常都会被捕捉并且处置到合适的responses。
  • 在将requests派发给响应的 handler 方法前,会对这些 requests 进行认证和权限验证,或者进行限流检查。

使用 APIView 类的方法基本上和使用 django 标准的 View 类相似。到来的 request 会被派发到合适 handler 方法,如 .get() 或者 .post()。此外,该类中可能包含一些用于控制API 策略特性的字段。

比如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import authentication, permissions
from django.contrib.auth.models import User

class ListUsers(APIView):
"""
View to list all users in the system.

* Requires token authentication.
* Only admin users are able to access this view.
"""
authentication_classes = [authentication.TokenAuthentication]
permission_classes = [permissions.IsAdminUser]

def get(self, request, format=None):
"""
Return a list of all users.
"""
usernames = [user.username for user in User.objects.all()]
return Response(usernames)

Note:

当面对APIView里的 methods、attributes,以及APIView、GenericAPIView、各种 Mixins、Viewsets 之间的关系时,你可能会觉有复杂。作为本文档的补充,Classy Django REST Framework 提供了网页版的参考,对于所有 Django REST Framework 基于类的 View 都提供了所有的 methods 和 attributes。http://www.cdrf.co/

API policy attributes

下面的 attributes 用于控制 API views 的一些可插拔件:

  • .renderer_classes
  • .parser_classes
  • .authentication_classes
  • .throttle_classes
  • .permission_classes
  • .content_negotiation_class

API policy instantiation methods

下面的一些方法被 REST framework 用来实例化各种可插拔的的 API 策略。一般而言,你不需要重写这些方法:

  • .get_renderers(self)
  • .get_parsers(self)
  • .get_authentications(self)
  • .get_throttles(self)
  • .get_permissions(self)
  • .get_content_negotiation(self)
  • .get_exception_handler(self)

API policy implementation methods

下述方法会在 requests 被派发到 handler method 之前被调用:

  • .check_permissions(self, request)
  • .check_throttles(self, request)
  • .perform_content_negotiation(self, request)

Dispatch methods

下述方法被 view的 .dispatch()方法直接调用。这些方法去执行任何在调用.get()、.post()、.put()、.patch() 和 .delete() 方法之前或者之后需要被执行的动作。

.initial(self, request, args, *kwargs)

执行需要在调用 handler method 之前发生的任何操作。这个方法被用于强化 permissions、throttling和进行 content negotiation。
一般而言,你不需要重写这个方法。

.handle_exception(self, exc)

从 handler method 抛出的任何异常都会传递到这个方法,改方法要么返回一个 Response 实例,要么继续抛出异常。

默认情况下,改方法会处理 rest_framework.exceptions.APIException 的任何子类,以及 Django 的 Http404 和 PermissionDenied 异常,返回一个合适的异常response。

如果你想为你的 API 定制 error responses,你应该继承这个方法。

.initialize_request(self, request, args, *kwargs)

确保传递到 handler method 的 request 对象是 DRF Request 实例,而不是常规的 Django HttpRequest。一般而言,你不需要重写这个方法。

.finalize_response(self, request, response, args, *kwargs)

确保从 handler methods 返回的任何 Response 会依据 content negotiation 渲染成正确的内容格式。一般而言,你不需要重写这个方法。

Function Based Views

“Saying [that class-based views] is always the superior solution is a mistake.”
— Nick Coghlan

REST framework 也允许你使用基于函数的view。它提供了一组装饰器,能够确保你的基于函数的view 接收到的是 REST framework 的 Request,而不是 Django 的 HttpRequest,并且允许返回 REST framework 的Response, 而不是 Django 的HttpResponse,也允许你对request如何处理进行配置。

@api_view()

@api_view(http_method_names=[‘GET’])
这个功能的核心是 api_view 装饰器,该装饰器包含一个你的view函数应该响应的HTTP 的方法的列表。比如下面的例子,你会写一个非常简单的view,并返回一些数据:

1
2
3
4
5
from rest_framework.decorators import api_view

@api_view()
def hello_world(request):
return Response({"message": "Hello, world!"})

这个view 会使用在settings中设置的默认 renderers,parsers,authentication等。默认情况下,只有 GET 请求会被接受。其他类型的请求,会被回应”405 Method Not Allowed“。如果不想这样,需要显示指出哪些方法会被允许。像这样:

1
2
3
4
5
@api_view(['GET', 'POST'])
def hello_world(request):
if request.method == 'POST':
return Response({"message": "Got some data!", "data": request.data})
return Response({"message": "Hello, world!"})

API policy decorators

为了重置默认设置,REST framework 提供了一组额外的装饰器可以添加到你的views 上。这些装饰器必须放在@api_view 的后面。比如,你想创建一个view,为其添加一个限流器 throttle ,以确保某个用户每天只能访问一次,可用@throttle_classes 装饰器,传递一个 throttle 列表。

1
2
3
4
5
6
7
8
9
10
from rest_framework.decorators import api_view, throttle_classes
from rest_framework.throttling import UserRateThrottle

class OncePerDayUserThrottle(UserRateThrottle):
rate = '1/day'

@api_view(['GET'])
@throttle_classes([OncePerDayUserThrottle])
def view(request):
return Response({"message": "Hello for today! See you tomorrow!"})

这些装饰器和APIView 子类的一些字段相对应,上文已经提到了。
这些可用的装饰器是:

  • @renderer_classes(…)
  • @parser_classes(…)
  • @authentication_classes(…)
  • @throttle_classes(…)
  • @permission_classes(…)

每个装饰器携带一个单一的参数,该参数必须是一个类列表或者元组。

View schema decorator

To override the default schema generation for function based views you may use the @schema decorator. This must come after (below) the @api_view decorator. For example:

1
2
3
4
5
6
7
8
9
10
11
from rest_framework.decorators import api_view, schema
from rest_framework.schemas import AutoSchema

class CustomAutoSchema(AutoSchema):
def get_link(self, path, method, base_url):
# override view introspection here...

@api_view(['GET'])
@schema(CustomAutoSchema())
def view(request):
return Response({"message": "Hello for today! See you tomorrow!"})

This decorator takes a single AutoSchema instance, an AutoSchema subclass instance or ManualSchema instance as described in the Schemas documentation. You may pass None in order to exclude the view from schema generation.

1
2
3
4
@api_view(['GET'])
@schema(None)
def view(request):
return Response({"message": "Will not appear in schema!"})