笔者从事的SaaS开发,对开发效率有着比较高的要求。从项目立项、原型设计评估、需求确定、前端设计、后台开发到最后的验收,几个星期完成一次迭代。在敏捷开发的指导下,开始推行前后端分离模式。前端专注于页面和交互,后端专注于API接口。后端提供API,涉及权限认证、参数校验、异常处理、分页等一系列问题。同时,这些功能在项目之间又存在共性,寻找合适的API脚手架十分迫切。笔者之前研究过Django Restful接口之Tastypie,相比较于Tastypie的简易、快速上手,Django REST Framework(DRF)需要更多的配置,同时功能更加强大,可以用于快速开发API接口。
1. 功能简介
- 支持 OAuth 认证
- 支持对 ORM 和非 ORM 数据源的序列化
- 丰富的定制层级:函数视图、类视图、视图集合
- 内置Mixins,可以用于快速组装
2. 基本概念
序列化是将 Python 数据结构转换为其它数据格式,比如将Django Model映射到Json。序列化是提供了数据的验证和渲染功能。其工作方式类似于 Django Form,基于 Field 进行字段验证。序列化之后的数据保存在 serializer.data中的,可以使用 SomeRenderer().render(serializer.data) 将其序列化为字符串对象作为 Response body 返回。
DRF 通过 View 提供 API 接口,一个 View 可以对应多个 Renderer,针对不同的渲染条件提供不同的输出格式(HTML/XML/JSON)。ViewSet 则是 View 的一个封装,一个 ViewSet 可以为同一个 URL 根据请求方法提供不同的接口。尤其是 ModelViewSet 会自动根据 Model 的定义生成 REST 接口和 URL,能够快速生成网站的一整套 API。
DRF使用Requests对象扩展了原生的HttpRequest,并提供了更灵活的请求处理。Requests对象的核心属性就是request.data,可以处理任意数据,接受POST、PUT和PATCH方法。
3. DRF处理流程
4. DRF应用
DRF的使用主要分为三步:定义资源 - 实现HTTP方法 - 配置URL。
4.1 安装配置
1
|
pip install djangorestframework
|
在settings.py的INSTALLED_APPS中加入:
1
2
3
4
5
|
INSTALLED_APPS = (
...
'rest_framework',
...
)
|
4.2 定义资源,实现序列化
models.py
1
2
3
4
5
|
from django.db import models
class Fruit(models.Model):
name = models.CharField(u'名称', default="", max_length=255)
price = models.FloatField(u"单价", default=0)
|
serializers.py
1
2
3
4
5
6
7
|
from rest_framework import serializers
from .models import Fruit
class FruitSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Fruit
fields = ('name', 'price')
|
4.3 继承View,重写HTTP Method
views.py
1
2
3
4
5
6
7
8
|
from rest_framework.viewsets import ModelViewSet
from .models import Fruit
from .serializers import FruitSerializer
class FruitViewSet(ModelViewSet):
queryset = Fruit.objects.all()
serializer_class = FruitSerializer
|
FruitViewSet直接继承ModelViewSet,ModelViewSet继承了一系列Mixins类的HTTP方法。如果需要自定义HTTP方法,可以继承APIView类,或者Mixins类,还可以完全自定义。
4.4 配置URL
urls.py
1
2
3
4
5
6
7
8
9
10
|
from django.conf.urls import patterns, url, include
from rest_framework import routers
from .views import FruitViewSet
router = routers.DefaultRouter()
router.register(r'fruit', FruitViewSet)
urlpatterns = [
url(r'^api/v1/', include(router.urls)),
]
|
配置到此,一个基本的API接口已经完成。访问http://localhost:8000/api/v1/,会显示:
4.5 权限
1
2
3
4
5
|
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly'
]
}
|
4.6 分页控制
1
2
3
|
class YourView(BaseView):
paginate_by = 10 # 覆盖 settings 中的默认分页
max_paginate_by = 100 # 限制最大分页大小
|
也可以动态地去判断最大分页大小:
1
2
3
4
5
|
class YouView(BaseView):
...
def paginate_queryset(self, queryset):
self.paginator.max_page_size = YOUR_PAGE_SIZE_LIMIT
return super(YouView, self).paginate_queryset(queryset)
|
4.7 外键处理
1
2
3
4
5
6
7
8
9
10
11
12
13
|
from rest_framework import serializers
class HospitalSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Hospital
fields = '__all__'
class HospitalPicSerializer(serializers.HyperlinkedModelSerializer):
hospital = HospitalSerializer()
class Meta:
model = HospitalPic
fields = '__all__'
|
4.8 流量限制
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_CLASSES': (
'rest_framework.throttling.AnonRateThrottle',
'rest_framework.throttling.UserRateThrottle'
),
'DEFAULT_THROTTLE_RATES': {
'anon': '100/day',
'user': '1000/day'
}
}
class ExampleView(APIView):
throttle_classes = (UserRateThrottle,)
def get(self, request, format=None):
content = {
'status': 'request was permitted'
}
return Response(content)
|
限制API查询的频率,可以根据用户不同,精确到每天、每小时、每分钟。
4.9 Mixins
Django-rest-framework为我们提供了许多现成的mixins,可以用于快速组合接口。
- GenericAPIView提供了view核心的功能
- ListModelMixin提供了.list()方法
- CreateModelMixin提供了.create()方法
在View函数中,可以这样使用:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
from .models import Fruit
from .serializers import FruitSerializer
from rest_framework import mixins
from rest_framework import generics
class FruitList(mixins.ListModelMixin, mixins.CreateModelMixin, generics.GenericAPIView):
queryset = Fruit.objects.all()
serializer_class = FruitSerializer
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
|
5. 参考
- http://www.django-rest-framework.org/
- http://www.django-rest-framework.org/api-guide/throttling/#throttling
- https://darkcooking.gitbooks.io/django-rest-framework-cn/content/
- https://blog.windrunner.me/python/web/django-rest-framework.html
- http://www.atjiang.com/django-rest-tut5-relationships-and-hyperlinked-apis/
- http://sillygod-blog.logdown.com/posts/663369
- https://segmentfault.com/a/1190000004401112