Django REST framework API Guide 之 Generic Views

最近几天,coronavirus 开始在欧美爆发,美股昨天发生本周的第二次熔断。整个世界的节奏都被打乱了,全球进入避险模式。

概览

在官网上,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 设置

Generic Views

“Django’s generic views… were developed as a shortcut for common usage patterns… They take certain common idioms and patterns found in view development and abstract them so that you can quickly write common views of data without having to repeat yourself.”

  • Django Documentation

基于类的Views 的一个关键好处是它允许你将一些可复用的行为组合起来。REST framework 同样利用了这一优势,它为一些常规的应用模式预先写好了一些views。

REST framework 提供的 generic views 允许你快速创建贴近数据库 model 的 API views。

如果这些 generic views 不能满足你的 API 需求,你可以不用他们而直接使用 APIView 类,或者重新使用 mixins 和 generic view 使用的基础类去组装你自己的可复用的 generic views。

Examples

在使用 generic views时,你会重写view,设置一些类字段。

1
2
3
4
5
6
7
8
9
from django.contrib.auth.models import User
from myapp.serializers import UserSerializer
from rest_framework import generics
from rest_framework.permissions import IsAdminUser

class UserList(generics.ListCreateAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
permission_classes = [IsAdminUser]

在一些复杂的情形下,你可能想去复写view 类中各种方法,比如:

1
2
3
4
5
6
7
8
9
10
class UserList(generics.ListCreateAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
permission_classes = [IsAdminUser]

def list(self, request):
# Note the use of `get_queryset()` instead of `self.queryset`
queryset = self.get_queryset()
serializer = UserSerializer(queryset, many=True)
return Response(serializer.data)

对于一些特别简单的api 需求,你可能你能想通过 .as_view()方法传递各种类 attributes。比如,你在配置url时,可以同时输入如下一些信息:

1
url(r'^/users/', ListCreateAPIView.as_view(queryset=User.objects.all(), serializer_class=UserSerializer), name='user-list')

API Reference

GenericAPIView

这个类拓展了 DRF 的 APIView 类,为标准的 list 和 detail view 添加了一些常用特性。

每个可用的generic views 都是通过一个或者多个mixin class 同 GenericAPIView联合创建而成。

Attributes

Basic settings

下面一些字段控制了view 类的基础行为:

  • queryset 用于从本view中返回多个实例。一般而言,你要么直接设置这个值,或者重写 get_queryset() 这个方法。如果你重写一个 view 方法,需要非常注意的是,你去调用 get_queryset() 而不是去直接获取这个值,毕竟 queryset 是一次性设定的值(不会改变),这些结果会被所有后续的requests缓存。
  • serializer_class serializer_class 会被用于验证和逆序列化输入数据,序列化输出数据。一般而言,你要么是设定这个值,要么重写 get_serializer_class()值。
  • lookup_field model 字段,用于去查找单一的 model 实例。默认情况下是 ‘pk’。当使用 hyperlinked APIs,如果你想用一个自定义的值,你需要确保在 API view 中和 serializer classes 中都有设置 lookup fields。
  • lookup_url_kwarg URL keyword 参数会被用于对象查询。URL 设置时,应该包含一个与这个值对应的参数。如果不设置该字段,会默认使用与 lookup_field 相同的值。
Pagination

当使用 list view 时,下面的字段用于控制分页:

  • pagination_class 在对列表结果进行分页时需要使用该字段。默认情况下,会使用在 ‘rest_framework.pagination.PageNumberPagination’ 设置的 DEFAULT_PAGINATION_CLASS。当设置 pagination_class=None 时,view 会取消分页功能。
  • filter_backends 过滤器列表,用于过滤 queryset。默认使用 DEFAULT_FILTER_BACKENDS 的值。

Methods

基础方法

get_queryset(self)

返回用于 list view 的 queryset,并且会作为 detail view 查询的一个基础。默认情况下,返回 .queryset 值。毕竟 queryset 是一次性设定的值(不会改变),这些结果会被所有后续的requests缓存。

该方法可以被重写,提供动态的行为,比如对不同用户的访问,返回不同的 queryset。
比如:

1
2
3
def get_queryset(self):
user = self.request.user
return user.accounts.all()
get_object(self)

返回一个用于 detail view 的实例对象。默认情况下是用 lookup_field 参数从基础 queryset 中过滤出来。为了满足复杂需求,该方法可能被重写,比如基于超过一个 url 参数的单个实例查询。

比如:

1
2
3
4
5
6
7
8
9
def get_object(self):
queryset = self.get_queryset()
filter = {}
for field in self.multiple_lookup_fields:
filter[field] = self.kwargs[field]

obj = get_object_or_404(queryset, **filter)
self.check_object_permissions(self.request, obj)
return obj

需要注意的是,如果你的API 不包含任何的 object 级别 permissions,你可以不调用 self.check_object_permissions,仅简单地返回从 get_object_or_404 查询道德实例。

filter_queryset(self, queryset)

对于给定的 queryset,
(这一块需要中停一下,需要先去理认证那块)