一. 什么是RESTful API以及Django RestFramework
1. 协议
API与用户的通信协议,总是使用HTTPS协议。
2. 域名
- 应该尽量将API部署在专用域名之下(存在跨域问题): https://api.example.com
 - 如果确定API很简单,不会有进一步扩展,可以考虑放在主域名下: https://example.org/api/
 
3. 版本
应该将API的版本号放入URL: https://api.example.com/v1/
另一种做法是,将版本号放在HTTP头信息中,但不如放入URL方便和直观。GitHub Developer Guide采用这种做法,跨域时会引发多次请求
4. 路径(Endpoint)
路径又称”终点”(endpoint),表示API的具体网址。
在RESTful架构中,每个网址代表一种资源(resource),所以网址中不能有动词,只能有名词,而且所用的名词往往与数据库的表格名对应。一般来说,数据库中的表都是同种记录的”集合”(collection),所以API中的名词也应该使用复数。
举例来说,有一个API提供动物园(zoo)的信息,还包括各种动物和雇员的信息,则它的路径应该设计成下面这样。
1  | https://api.example.com/v1/zoos  | 
5. HTTP动词
对于资源的具体操作类型,由HTTP动词表示。
常用的HTTP动词有下面五个(括号里是对应的SQL命令)
1  | GET(SELECT):从服务器取出资源(一项或多项)  | 
还有两个不常用的HTTP动词
1  | HEAD:获取资源的元数据  | 
下面是一些栗子:
1  | GET /zoos:列出所有动物园  | 
6. 过滤信息(Filtering)
如果记录数量很多,服务器不可能都将它们返回给用户.API应该提供参数,过滤返回结果
常见的参数形式如下:
1  | ?limit=10:指定返回记录的数量  | 
参数的设计允许存在冗余,即允许API路径和URL参数偶尔有重复。比如,GET /zoo/ID/animals 与 GET /animals?zoo_id=ID 的含义是相同的。
7. 状态码(Status Codes)
服务器向用户返回的状态码和提示信息,常见的有以下一些(方括号中是该状态码对应的HTTP动词)
1  | 200 OK - [GET]:服务器成功返回用户请求的数据,该操作是幂等的(Idempotent)。  | 
注意: 状态码的完全列表参见这里
8. 错误处理(Error handling)
如果状态码是4xx,就应该向用户返回出错信息。一般来说,返回的信息中将error作为键名,出错信息作为键值即可。
1  | {  | 
9. 返回结果
针对不同操作,服务器向用户返回的结果应该符合以下规范
1  | GET /collection:返回资源对象的列表(数组)  | 
10. Hypermedia API
RESTful API最好做到Hypermedia,即返回结果中提供链接,连向其他API方法,使得用户不查文档,也知道下一步应该做什么。
比如,当用户向api.example.com的根目录发出请求,会得到这样一个文档:
1  | {"link": {  | 
上面代码表示,文档中有一个link属性,用户读取这个属性就知道下一步该调用什么API了。rel表示这个API与当前网址的关系(collection关系,并给出该collection的网址),href表示API的路径,title表示API的标题,type表示返回类型。
Hypermedia API的设计被称为HATEOAS。Github的API就是这种设计,访问api.github.com会得到一个所有可用API的网址列表。
1  | {  | 
从上面可以看到,如果想获取当前用户的信息,应该去访问\api.github.com/user,然后就得到了下面结果:
1  | {  | 
上面代码表示,服务器给出了提示信息,以及文档的网址
11. 其他
- API的身份认证应该使用OAuth 2.0框架。
 - 服务器返回的数据格式,应该尽量使用JSON,避免使用XML。
 
RESTful API 设计指南 - 阮一峰的网络日志
GitHub - aisuhua/restful-api-design-references: RESTful API 设计参考文献列表  
二. 基于Django的实现
2.1 路由系统
1  | from app01 import views  | 
2.2 CBV试图
1  | class UserView(View):  | 
三. 基于Django Rest Framework 框架实现
3.1 基本流程
路由:URL.py
1  | from django.conf.urls import url  | 
试图: views.py
1  | from rest_framework.views import APIView  | 
⚠️ : 以上是Django rest framework框架基本流程,重要的功能是在APIView的dispatch中触发,也是框架的源码入口
3.2 认证和授权
3.2.1 基于token的验证
1  | urls.py  | 
1  | $ cat models.py  | 
1  | $ cat views.py  | 
1  | $ cat app01/utils/auth.py  | 
1  | $ cat settings.py  | 
验证:
- 首先在数据库中构造请求登录的用户名密码
 - 构造数据获取token信息
1
2
3
4
5
6
7
8$ curl -X POST \
> http://127.0.0.1:8001/api/v1/auth/ \
> -H 'Cache-Control: no-cache' \
> -H 'Content-Type: application/json' \
> -H 'Postman-Token: 8439ce0a-94ff-3970-c8c2-4c6bc200bb4f' \
> -d '{"username": "eric","password":"123"}'
返回:
{"code": 1000, "token": "4c90f696-008b-4e30-86fa-4b00b6fc9237"} - 携带token访问user接口,获取用户信息
1
2
3
4
5> 'http://127.0.0.1:8001/api/v1/user/?token=4c90f696-008b-4e30-86fa-4b00b6fc9237' \
> -H 'Cache-Control: no-cache' \
> -H 'Postman-Token: 1b4125ad-7ad1-1cca-70fd-6a00cca16d96'
返回:
user.get: eric 
3.2.2 基于请求头认证
1  | $ cat urls.py  | 
1  | $ cat app01/utils/auth.py  | 
1  | $ cat views.py  | 
验证:
- 首先在数据库中构造请求登录的用户名密码
 - 构造数据获取token信息,建议使用postman进行构造测试
1
2
3
4
5
6
7$ curl -X GET \
http://127.0.0.1:8001/api/v1/user/ \
-H 'Authorization: Basic c2h1a2U6MTIz' \
-H 'Cache-Control: no-cache' \
-H 'Postman-Token: ba3f99ce-da6a-7e5f-3e0b-3c79129ecca1'
返回:
user.get: shuke,token: 2d3af34a-1598-4d5a-af56-7f29a706a26e3.2.3 多个认证规则
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19$ cat urls.py
from django.contrib import admin
from django.urls import path
from django.conf.urls import url, include
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^api/(?P<version>\w+)/', include('app01.urls')),
]
$ cat app01/urls.py
from django.conf.urls import url
from app01 import views
urlpatterns = [
url(r'^auth/$', views.AuthView.as_view()),
url(r'^user/$', views.UserView.as_view()),
]1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82$ cat app01/utils/auth.py
#! /usr/bin/env python
# -*- coding: utf-8 -*-
# __author__ = "shuke"
# Date: 2018/6/1
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
from rest_framework import exceptions
from app01 import models
class TokenAuthtication(BaseAuthentication):
def authenticate(self, request):
"""
:param request:
:return:
(user,auth) 表示认证成功,并将元组分别赋值给request.user/request.auth
:raise AuthenticationFailed('认证失败') 表示认证失败
"""
token = request.query_params.get('token')
if not token:
raise AuthenticationFailed("用户Token未携带")
token_obj = models.UserToken.objects.filter(token=token).first()
if not token_obj:
raise AuthenticationFailed("Token已失效或错误")
return (token_obj.user.username, token_obj)
class HeaderAuthentication(BaseAuthentication):
def authenticate(self, request):
"""
用户认证,如果验证成功后返回元组: (用户,用户Token)
:param request:
:return:
None,表示跳过该验证;
如果跳过了所有认证,默认用户和Token和使用配置文件进行设置
self._authenticator = None
if api_settings.UNAUTHENTICATED_USER:
self.user = api_settings.UNAUTHENTICATED_USER()
else:
self.user = None
if api_settings.UNAUTHENTICATED_TOKEN:
self.auth = api_settings.UNAUTHENTICATED_TOKEN()
else:
self.auth = None
(user,token)表示验证通过并设置用户名和Token;
AuthenticationFailed异常
"""
import base64
import uuid
auth = request.META.get('HTTP_AUTHORIZATION', b'')
if auth:
auth = auth.encode('utf-8')
auth = auth.split()
if not auth or auth[0].lower() != b'basic':
raise exceptions.AuthenticationFailed('验证失败')
if len(auth) != 2:
raise exceptions.AuthenticationFailed('验证失败')
username, part, password = base64.b64decode(auth[1]).decode('utf-8').partition(':')
try:
obj = models.UserInfo.objects.filter(username=username, password=password).first()
if not obj:
raise exceptions.AuthenticationFailed('用户名或密码错误')
token = str(uuid.uuid4())
token_obj, status = models.UserToken.objects.update_or_create(user=obj, defaults={"token": token})
return (token_obj.user.username, token_obj)
except Exception as e:
print("Error: ", e)
def authenticate_header(self, request):
"""
Return a string to be used as the value of the `WWW-Authenticate`
header in a `401 Unauthenticated` response, or `None` if the
authentication scheme should return `403 Permission Denied` responses.
"""
return 'Basic realm=api'验证:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44$ cat views.py
from django.shortcuts import render, HttpResponse
# Create your views here.
from django.http import JsonResponse
from rest_framework.views import APIView
from app01.utils.auth import HeaderAuthentication, TokenAuthtication
from app01 import models
import uuid
class AuthView(APIView):
authentication_classes = []
def post(self, request, *args, **kwargs):
response = {'code': 1000}
user = request.data.get('username')
pwd = request.data.get('password')
obj = models.UserInfo.objects.filter(username=user, password=pwd).first()
if not obj:
response['code'] = 1001
response['msg'] = '用户或密码错误'
return JsonResponse(response, json_dumps_params={'ensure_ascii': False})
try:
token = str(uuid.uuid4())
models.UserToken.objects.update_or_create(user=obj, defaults={"token": token})
response['token'] = token
except Exception as e:
print("Error: ", e)
return JsonResponse(response, json_dumps_params={'ensure_ascii': False})
class UserView(APIView):
authentication_classes = [HeaderAuthentication, TokenAuthtication]
def get(self, request, *args, **kwargs):
print(request.user)
print(request.auth)
return HttpResponse('user.get: %s,token: %s' % (request.user, request.auth))
def post(self, request, *args, **kwargs):
return HttpResponse('user.post') - 首先在数据库中构造请求登录的用户名密码
 - 构造数据获取token信息,建议使用postman进行构造测试
1
2
3
4
5
6
7$ curl -X GET \
http://127.0.0.1:8001/api/v1/user/ \
-H 'Authorization: Basic c2h1a2U6MTIz' \
-H 'Cache-Control: no-cache' \
-H 'Postman-Token: f91805b7-c742-b713-0010-c75cdbfbb24a'
返回:
user.get: shuke,token: 9c3ca96a-ac03-4207-aaec-20433bae6058 
3.2.4 认证和权限
路由
1  | $ cat urls.py  | 
model
1  | from django.db import models  | 
认证
1  | $ cat app01/utils/auth.py  | 
权限
1  | $ cat app01/utils/permission.py  | 
试图函数
1  | from django.shortcuts import render, HttpResponse  | 
验证:
全局应用权限
上述操作中均是对单独视图进行特殊配置,如果想要对全局进行配置,则需要再配置文件中写入即可。
1  | REST_FRAMEWORK = {  | 
路由
1  | $ cat urls.py  | 
试图
1  | $ cat views.py  | 
3.3 用户访问次数/频率限制
3.3.1 基于用户IP访问限制频率
路由
1  | $ cat urls.py  | 
试图
1  | #!/usr/bin/env python  | 
3.3.2 基于用户IP控制访问频率(利用Django缓存)
全局配置
1  | REST_FRAMEWORK = {  | 
路由
1  | from django.conf.urls import url, include  | 
试图
1  | #!/usr/bin/env python  | 
3.3.3 在试图中限制请求频率
全局配置
1  | REST_FRAMEWORK = {  | 
路由
1  | from django.conf.urls import url, include  | 
试图
1  | #!/usr/bin/env python  | 
3.3.4 匿名时用IP限制+登录时用Token限制
全局配置
1  | REST_FRAMEWORK = {  | 
路由
1  | from django.conf.urls import url, include  | 
试图
1  | # !/usr/bin/env python  | 
3.3.5 全局使用
1  | REST_FRAMEWORK = {  | 
3.4 版本
共6个类
- BaseVersioning
 - AcceptHeaderVersioning
 - URLPathVersioning
 - NamespaceVersioning
 - HostNameVersioning
 - QueryParameterVersioning
而且还可以看到BaseVersioning类是其余5个类的父类,并且这其余的5个类,每个类中都有一个determine_version方法,在项目的视图函数中导入其中任意一个类,打印versioning_class输出结果:1
2
3
4
5
6
7
8
9
10
11
12from django.shortcuts import render,HttpResponse
from rest_framework.views import APIView
from django.views import View
from rest_framework.versioning import QueryParameterVersioning
class UsersView(APIView):
versioning_class=QueryParameterVersioning
def get(self,request,*args,**kwargs):
print(self.versioning_class) #打印versioning_class
return HttpResponse("aaaa")所以versioning_class是一个类,并且versioning_class类中有一个determine_version方法1
<class 'rest_framework.versioning.QueryParameterVersioning'>
 
3.4.1 基于URL的GET传参方式
如:/users?version=v1
全局配置
1  | REST_FRAMEWORK = {  | 
路由
1  | from django.contrib import admin  | 
试图
1  | from rest_framework.views import APIView  | 
3.4.2 基于URL的正则方式
如: /v1/users/
1  | REST_FRAMEWORK = {  | 
路由
1  | from django.conf.urls import url, include  | 
试图
1  | #!/usr/bin/env python  | 
3.4.3 基于accept请求头方式
如:Accept: application/json; version=1.0
1  | REST_FRAMEWORK = {  | 
路由
1  | from django.conf.urls import url, include  | 
试图
1  | # !/usr/bin/env python  | 
3.4.4 基于主机名方式
如: v1.example.com
1  | ALLOWED_HOSTS = ['*']  | 
路由
1  | from django.conf.urls import url, include  | 
试图
1  | # !/usr/bin/env python  | 
3.4.5 基于Django路由系统的namespace
如: example.com/v1/users/
1  | REST_FRAMEWORK = {  | 
路由
1  | from django.conf.urls import url, include  | 
试图
1  | # !/usr/bin/env python  | 
REST_FRAMEWORK = {
    ‘DEFAULT_VERSIONING_CLASS’:”rest_framework.versioning.URLPathVersioning”,
    ‘DEFAULT_VERSION’: ‘v1’,
    ‘ALLOWED_VERSIONS’: [‘v1’, ‘v2’],
    ‘VERSION_PARAM’: ‘version’
}
1  | #### 3.4.7 自定义版本控制方案  | 
class XAPIVersionScheme(versioning.BaseVersioning):
    def determine_version(self, request, args, *kwargs):
        return request.META.get(‘HTTP_X_API_VERSION’, None)
1  | 如果你的版本控制方案基于请求 URL,则还需要更改版本化 URL 的确定方式。为了做到这一点,你应该重写类的 .reverse()方法。有关示例,请参阅源代码。  | 
from django.conf.urls import url, include
from web.views.s5_parser import TestView
urlpatterns = [
    url(r’test/‘, TestView.as_view(), name=’test’),
]
1  | 试图  | 
!/usr/bin/env python
-- coding:utf-8 --
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.parsers import JSONParser
class TestView(APIView):
    parser_classes = [JSONParser, ]
def post(self, request, *args, **kwargs):
    print(request.content_type)
    # 获取请求的值,并使用对应的JSONParser进行处理
    print(request.data)
    # application/x-www-form-urlencoded 或 multipart/form-data时,request.POST中才有值
    print(request.POST)
    print(request.FILES)
    return Response('POST请求,请求内容: %s' % request.data)
def put(self, request, *args, **kwargs):
    return Response('PUT请求,响应内容,解析器')1  | #### 3.5.2 仅处理请求头content-type为application/x-www-form-urlencoded的请求体  | 
from django.conf.urls import url, include
from web.views import TestView
urlpatterns = [
    url(r’test/‘, TestView.as_view(), name=’test’),
]
1  | 试图  | 
#!/usr/bin/env python
-- coding:utf-8 --
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.request import Request
from rest_framework.parsers import FormParser
class TestView(APIView):
    parser_classes = [FormParser, ]
def post(self, request, *args, **kwargs):
    print(request.content_type)
    # 获取请求的值,并使用对应的JSONParser进行处理
    print(request.data)
    # application/x-www-form-urlencoded 或 multipart/form-data时,request.POST中才有值
    print(request.POST)
    print(request.FILES)
    return Response('POST请求,响应内容')
def put(self, request, *args, **kwargs):
    return Response('PUT请求,响应内容')1  | #### 3.5.3 仅处理请求头content-type为multipart/form-data的请求体  | 
from django.conf.urls import url, include
from web.views import TestView
urlpatterns = [
    url(r’test/‘, TestView.as_view(), name=’test’),
]
1  | 试图  | 
!/usr/bin/env python
-- coding:utf-8 --
from django.shortcuts import render
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.parsers import MultiPartParser
class TestView(APIView):
    authentication_classes = []
    permission_classes = []
    parser_classes = [MultiPartParser, ]
def get(self, request, *args, **kwargs):
    return render(request, 'test.html')
def post(self, request, *args, **kwargs):
    print(request.content_type)
    # 获取请求的值,并使用对应的JSONParser进行处理
    print(request.data)
    # application/x-www-form-urlencoded 或 multipart/form-data时,request.POST中才有值
    print(request.POST)
    print(request.FILES)
    return Response('POST请求,响应内容')
def put(self, request, *args, **kwargs):
    return Response('PUT请求,响应内容')1  | 模版文件  | 
1  | #### 3.5.4 仅上传文件  | 
urlpatterns = [
    url(r’test/(?P
]
1  | 试图  | 
!/usr/bin/env python
-- coding:utf-8 --
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.parsers import FileUploadParser
class TestView(APIView):
    authentication_classes = []
    permission_classes = []
    parser_classes = [FileUploadParser, ]
def get(self, request, *args, **kwargs):
    return render(request, 'test.html')
def post(self, request, filename, *args, **kwargs):
    print(filename)
    print(request.content_type)
    # 获取请求的值,并使用对应的JSONParser进行处理
    print(request.data)
    # application/x-www-form-urlencoded 或 multipart/form-data时,request.POST中才有值
    print(request.POST)
    print(request.FILES)
    return Response('POST请求,响应内容')
def put(self, request, *args, **kwargs):
    return Response('PUT请求,响应内容')1  | 模版文件  | 
1  | #### 3.5.5 同时多个Parser  | 
urlpatterns = [
    url(r’test/‘, TestView.as_view(), name=’test’),
]
1  | 试图  | 
!/usr/bin/env python
-- coding:utf-8 --
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.parsers import JSONParser, FormParser, MultiPartParser
class TestView(APIView):
    parser_classes = [JSONParser, FormParser, MultiPartParser, ]
def post(self, request, *args, **kwargs):
    print(request.content_type)
    # 获取请求的值,并使用对应的JSONParser进行处理
    print(request.data)
    # application/x-www-form-urlencoded 或 multipart/form-data时,request.POST中才有值
    print(request.POST)
    print(request.FILES)
    return Response('POST请求,响应内容')
def put(self, request, *args, **kwargs):
    return Response('PUT请求,响应内容')1  | #### 3.5.6 全局应用  | 
REST_FRAMEWORK = {
    ‘DEFAULT_PARSER_CLASSES’:[
        ‘rest_framework.parsers.JSONParser’
        ‘rest_framework.parsers.FormParser’
        ‘rest_framework.parsers.MultiPartParser’
    ]
}
1  | 路由  | 
from django.conf.urls import url, include
from web.views import TestView
urlpatterns = [
    url(r’test/‘, TestView.as_view(), name=’test’),
]
1  | 试图  | 
#!/usr/bin/env python
-- coding:utf-8 --
from rest_framework.views import APIView
from rest_framework.response import Response
class TestView(APIView):
    def post(self, request, args, *kwargs):
        print(request.content_type)
    # 获取请求的值,并使用对应的JSONParser进行处理
    print(request.data)
    # application/x-www-form-urlencoded 或 multipart/form-data时,request.POST中才有值
    print(request.POST)
    print(request.FILES)
    return Response('POST请求,响应内容')
def put(self, request, *args, **kwargs):
    return Response('PUT请求,响应内容')1  | ⚠️ 个别特殊的值可以通过Django的request对象 request\.\_request 来进行获取  | 
from django.conf.urls import url, include
from app01.views import TestView
urlpatterns = [
    url(r’^test/‘, TestView.as_view(), name=’test’),
]
1  | models  | 
from django.db import models
Create your models here.
from django.db import models
class UserInfo(models.Model):
    user_type_choices = (
        (1, ‘普通用户’),
        (2, ‘管理员’),
        (3, ‘超级管理员’),
    )
    username = models.CharField(max_length=32)
    password = models.CharField(max_length=64)
    user_type = models.IntegerField(choices=user_type_choices, default=1)
class UserToken(models.Model):
    user = models.OneToOneField(‘UserInfo’, on_delete=True)
    token = models.CharField(max_length=64)
1  | 试图  | 
!/usr/bin/env python
-- coding:utf-8 --
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers
from . import models
class PasswordValidator(object):
    def init(self, base):
        self.base = base
def __call__(self, value):
    if value != self.base:
        message = 'This field must be %s.' % self.base
        raise serializers.ValidationError(message)
def set_context(self, serializer_field):
    """
    This hook is called by the serializer instance,
    prior to the validation call being made.
    """
    # 执行验证之前调用,serializer_fields是当前字段对象
    passclass UserSerializer(serializers.Serializer):
    user_type = serializers.IntegerField()
    username = serializers.CharField(min_length=3)
    password = serializers.CharField(error_messages={‘required’: ‘密码不能为空’}, validators=[PasswordValidator(‘666’)])
class TestView(APIView):
    authentication_classes = []
    permission_classes = []
def get(self, request, *args, **kwargs):
    # 序列化,将数据库查询字段序列化为字典
    data_list = models.UserInfo.objects.all()
    ser = UserSerializer(instance=data_list, many=True)
    # 或
    # obj = models.UserInfo.objects.all().first()
    # ser = UserSerializer(instance=obj, many=False)
    return Response(ser.data)
def post(self, request, *args, **kwargs):
    # 验证,对请求发来的数据进行验证
    ser = UserSerializer(data=request.data)
    if ser.is_valid():
        print(ser.validated_data)
    else:
        print(ser.errors)
    return Response('POST请求,响应内容')1  | POST验证:  | 
curl -X POST 
  http://127.0.0.1:8001/test/ 
  -H ‘Cache-Control: no-cache’ 
  -H ‘Content-Type: application/json’ 
  -H ‘Postman-Token: 4ea5d1d1-e3b1-38a3-19f6-25070ace2342’ 
  -d ‘{
        “user_type”: 2,
        “username”: “python”,
        “password”: “666”
    }’
1  | #### 3.6.2 基于Model自动生成字段  | 
from django.conf.urls import url, include
from web.views.s6_serializers import TestView
urlpatterns = [
    url(r’test/‘, TestView.as_view(), name=’test’),
]
1  | 试图  | 
#!/usr/bin/env python
-- coding:utf-8 --
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers
from .. import models
class PasswordValidator(object):
    def init(self, base):
        self.base = str(base)
def __call__(self, value):
    if value != self.base:
        message = 'This field must be %s.' % self.base
        raise serializers.ValidationError(message)
def set_context(self, serializer_field):
    """
    This hook is called by the serializer instance,
    prior to the validation call being made.
    """
    # 执行验证之前调用,serializer_fields是当前字段对象
    passclass ModelUserSerializer(serializers.ModelSerializer):
user = serializers.CharField(max_length=32)
class Meta:
    model = models.UserInfo
    fields = "__all__"
    # fields = ['user', 'pwd', 'ut']
    depth = 2
    extra_kwargs = {'user': {'min_length': 6}, 'pwd': {'validators': [PasswordValidator(666), ]}}
    # read_only_fields = ['user']class TestView(APIView):
    def get(self, request, args, *kwargs):
    # 序列化,将数据库查询字段序列化为字典
    data_list = models.UserInfo.objects.all()
    ser = ModelUserSerializer(instance=data_list, many=True)
    # 或
    # obj = models.UserInfo.objects.all().first()
    # ser = UserSerializer(instance=obj, many=False)
    return Response(ser.data)
def post(self, request, *args, **kwargs):
    # 验证,对请求发来的数据进行验证
    print(request.data)
    ser = ModelUserSerializer(data=request.data)
    if ser.is_valid():
        print(ser.validated_data)
    else:
        print(ser.errors)
    return Response('POST请求,响应内容')1  | #### 3.6.3 生成URL  | 
from django.conf.urls import url, include
from web.views.s6_serializers import TestView
urlpatterns = [
    url(r’test/‘, TestView.as_view(), name=’test’),
    url(r’detail/(?P
]
1  | models  | 
from django.db import models
Create your models here.
from django.db import models
class UserInfo(models.Model):
    user_type_choices = (
        (1, ‘普通用户’),
        (2, ‘管理员’),
        (3, ‘超级管理员’),
    )
    username = models.CharField(max_length=32)
    password = models.CharField(max_length=64)
    user_type = models.IntegerField(choices=user_type_choices, default=1)
class UserToken(models.Model):
    user = models.OneToOneField(‘UserInfo’, on_delete=True)
    token = models.CharField(max_length=64)
1  | 试图  | 
!/usr/bin/env python
-- coding:utf-8 --
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers
from . import models
class PasswordValidator(object):
    def init(self, base):
        self.base = str(base)
def __call__(self, value):
    if value != self.base:
        message = 'This field must be %s.' % self.base
        raise serializers.ValidationError(message)
def set_context(self, serializer_field):
    """
    This hook is called by the serializer instance,
    prior to the validation call being made.
    """
    # 执行验证之前调用,serializer_fields是当前字段对象
    passclass ModelUserSerializer(serializers.ModelSerializer):
    user_type = serializers.HyperlinkedIdentityField(view_name=’detail’)
class Meta:
    model = models.UserInfo
    fields = "__all__"
    extra_kwargs = {
        'username': {'min_length': 6},
        'pasword': {'validators': [PasswordValidator(666), ]},
    }class TestView(APIView):
    authentication_classes = []
    permission_classes = []
def get(self, request, *args, **kwargs):
    # 序列化,将数据库查询字段序列化为字典
    data_list = models.UserInfo.objects.all()
    ser = ModelUserSerializer(instance=data_list, many=True, context={'request': request})
    # 或
    # obj = models.UserInfo.objects.all().first()
    # ser = UserSerializer(instance=obj, many=False)
    return Response(ser.data)
def post(self, request, *args, **kwargs):
    # 验证,对请求发来的数据进行验证
    print(request.data)
    ser = ModelUserSerializer(data=request.data)
    if ser.is_valid():
        print(ser.validated_data)
    else:
        print(ser.errors)
    return Response('POST请求,响应内容')1  | 请求如下图所示:  | 
from django.conf.urls import url, include
from app01.views import TestView
urlpatterns = [
    url(r’test/‘, TestView.as_view(), name=’test’),
    url(r’detail/(?P
]
1  | 试图  | 
!/usr/bin/env python
-- coding:utf-8 --
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers
from . import models
class PasswordValidator(object):
    def init(self, base):
        self.base = str(base)
def __call__(self, value):
    if value != self.base:
        message = 'This field must be %s.' % self.base
        raise serializers.ValidationError(message)
def set_context(self, serializer_field):
    """
    This hook is called by the serializer instance,
    prior to the validation call being made.
    """
    # 执行验证之前调用,serializer_fields是当前字段对象
    passclass ModelUserSerializer(serializers.HyperlinkedModelSerializer):
    ll = serializers.HyperlinkedIdentityField(view_name=’detail-info’)
    tt = serializers.CharField(required=False)
class Meta:
    model = models.UserInfo
    fields = "__all__"
    list_serializer_class = serializers.ListSerializer
    extra_kwargs = {
        'username': {'min_length': 6},
        'password': {'validators': [PasswordValidator(666), ]},
        'url': {'view_name': 'detail-info'},
        'ut': {'view_name': 'detail-info'},
    }class TestView(APIView):
    def get(self, request, args, *kwargs):
        # # 序列化,将数据库查询字段序列化为字典
        data_list = models.UserInfo.objects.all()
        ser = ModelUserSerializer(instance=data_list, many=True, context={‘request’: request})
        # # 如果Many=True
        # # 或
        # # obj = models.UserInfo.objects.all().first()
        # # ser = UserSerializer(instance=obj, many=False)
        return Response(ser.data)
def post(self, request, *args, **kwargs):
    # 验证,对请求发来的数据进行验证
    print(request.data)
    ser = ModelUserSerializer(data=request.data)
    if ser.is_valid():
        print(ser.validated_data)
    else:
        print(ser.errors)
    return Response('POST请求,响应内容')1  | #### 3.7 分页  | 
from django.conf.urls import url, include
from app01.views import UserViewSet
urlpatterns = [
    url(r’test/‘, UserViewSet.as_view(), name=’test’),
]
1  | 试图  | 
!/usr/bin/env python
-- coding:utf-8 --
from rest_framework.views import APIView
from rest_framework import serializers
from . import models
from rest_framework.pagination import PageNumberPagination
class StandardResultsSetPagination(PageNumberPagination):
    # 默认每页显示的数据条数
    page_size = 1
    # 获取URL参数中设置的每页显示数据条数
    page_size_query_param = ‘page_size’
# 获取URL参数中传入的页码key
page_query_param = 'page'
# 最大支持的每页显示的数据条数
max_page_size = 1class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.UserInfo
        fields = “all“
class UserViewSet(APIView):
    def get(self, request, args, *kwargs):
        user_list = models.UserInfo.objects.all().order_by(‘-id’)
# 实例化分页对象,获取数据库中的分页数据
paginator = StandardResultsSetPagination()
page_user_list = paginator.paginate_queryset(user_list, self.request, view=self)
# 序列化对象
serializer = UserSerializer(page_user_list, many=True)
# 生成分页和数据
response = paginator.get_paginated_response(serializer.data)
return response1  | 访问验证:  | 
http://127.0.0.1:8001/test/?page=1
http://127.0.0.1:8001/test/?page=2
1  | #### 3.7.2 位置和个数进行分页  | 
from django.conf.urls import url, include
from app01.views import UserViewSet
urlpatterns = [
    url(r’test/‘, UserViewSet.as_view(), name=’test’),
]
1  | 试图  | 
!/usr/bin/env python
-- coding:utf-8 --
from rest_framework.views import APIView
from rest_framework import serializers
from . import models
from rest_framework.pagination import PageNumberPagination, LimitOffsetPagination
class StandardResultsSetPagination(LimitOffsetPagination):
    # 默认每页显示的数据条数
    default_limit = 2
    # URL中传入的显示数据条数的参数
    limit_query_param = ‘limit’
    # URL中传入的数据位置的参数
    offset_query_param = ‘offset’
    # 最大每页显得条数
    max_limit = None
class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.UserInfo
        fields = “all“
class UserViewSet(APIView):
    def get(self, request, args, *kwargs):
        user_list = models.UserInfo.objects.all().order_by(‘-id’)
# 实例化分页对象,获取数据库中的分页数据
paginator = StandardResultsSetPagination()
page_user_list = paginator.paginate_queryset(user_list, self.request, view=self)
# 序列化对象
serializer = UserSerializer(page_user_list, many=True)
# 生成分页和数据
response = paginator.get_paginated_response(serializer.data)
return response1  | 验证:  | 
from django.conf.urls import url, include
from app01.views import UserViewSet
urlpatterns = [
    url(r’test/‘, UserViewSet.as_view(), name=’test’),
]
1  | 试图  | 
!/usr/bin/env python
-- coding:utf-8 --
from rest_framework.views import APIView
from rest_framework import serializers
from . import models
from rest_framework.pagination import PageNumberPagination, LimitOffsetPagination, CursorPagination
class StandardResultsSetPagination(CursorPagination):
    # URL传入的游标参数
    cursor_query_param = ‘cursor’
    # 默认每页显示的数据条数
    page_size = 10
    # URL传入的每页显示条数的参数
    page_size_query_param = ‘page_size’
    # 每页显示数据最大条数
    max_page_size = 1000
# 根据ID从大到小排列
ordering = "id"class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.UserInfo
        fields = “all“
class UserViewSet(APIView):
    def get(self, request, args, *kwargs):
        user_list = models.UserInfo.objects.all().order_by(‘-id’)
# 实例化分页对象,获取数据库中的分页数据
paginator = StandardResultsSetPagination()
page_user_list = paginator.paginate_queryset(user_list, self.request, view=self)
# 序列化对象
serializer = UserSerializer(page_user_list, many=True)
# 生成分页和数据
response = paginator.get_paginated_response(serializer.data)
return response1  | 
  | 
from django.conf.urls import url, include
from web.views import s11_render
urlpatterns = [
    url(r’^test/$’, s11_render.TestView.as_view()),
    url(r’^test.(?P
    url(r’^test/(?P
    url(r’^test/(?P
]
1  | 试图  | 
from rest_framework.views import APIView
from rest_framework.response import Response
from .. import models
class TestView(APIView):
    def get(self, request, args, *kwargs):
        print(kwargs)
        print(self.renderer_classes)
        return Response(‘…’)
1  | #### 3.8.2 半自动路由  | 
from django.conf.urls import url, include
from web.views import s10_generic
urlpatterns = [
    url(r’^test/$’, s10_generic.UserViewSet.as_view({‘get’: ‘list’, ‘post’: ‘create’})),
    url(r’^test/(?P
        {‘get’: ‘retrieve’, ‘put’: ‘update’, ‘patch’: ‘partial_update’, ‘delete’: ‘destroy’})),
]
1  | 试图  | 
#!/usr/bin/env python
-- coding:utf-8 --
from rest_framework.viewsets import ModelViewSet
from rest_framework import serializers
from .. import models
class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.UserInfo
        fields = “all“
class UserViewSet(ModelViewSet):
    queryset = models.UserInfo.objects.all()
    serializer_class = UserSerializer
1  | #### 3.8.3 全自动路由  | 
from django.conf.urls import url, include
from rest_framework import routers
from web.views import s10_generic
router = routers.DefaultRouter()
router.register(r’users’, s10_generic.UserViewSet)
urlpatterns = [
    url(r’^’, include(router.urls)),
]
1  | 试图  | 
from rest_framework.viewsets import ModelViewSet
from rest_framework import serializers
from .. import models
class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.UserInfo
        fields = “all“
class UserViewSet(ModelViewSet):
    queryset = models.UserInfo.objects.all()
    serializer_class = UserSerializer
1  | ### 3.9 试图  | 
from django.conf.urls import url, include
from web.views.s7_viewset import TestView
urlpatterns = [
    url(r’test/‘, TestView.as_view({‘get’:’list’}), name=’test’),
    url(r’detail/(?P
]
1  | 试图  | 
#!/usr/bin/env python
-- coding:utf-8 --
from rest_framework import viewsets
from rest_framework.response import Response
class TestView(viewsets.GenericViewSet):
    def list(self, request, args, *kwargs):
        return Response(‘…’)
def add(self, request, *args, **kwargs):
    pass
def delete(self, request, *args, **kwargs):
    pass
def edit(self, request, *args, **kwargs):
    pass1  | #### 3.9.2 ModelViewSet(自定义URL)  | 
from django.conf.urls import url, include
from web.views import s10_generic
urlpatterns = [
    url(r’^test/$’, s10_generic.UserViewSet.as_view({‘get’: ‘list’, ‘post’: ‘create’})),
    url(r’^test/(?P
        {‘get’: ‘retrieve’, ‘put’: ‘update’, ‘patch’: ‘partial_update’, ‘delete’: ‘destroy’})),
]
1  | 试图  | 
#!/usr/bin/env python
-- coding:utf-8 --
from rest_framework.viewsets import ModelViewSet
from rest_framework import serializers
from .. import models
class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.UserInfo
        fields = “all“
class UserViewSet(ModelViewSet):
    queryset = models.UserInfo.objects.all()
    serializer_class = UserSerializer
1  | #### 3.9.3 ModelViewSet(rest framework路由)  | 
from django.conf.urls import url, include
from rest_framework import routers
from app01 import views
router = routers.DefaultRouter()
router.register(r’users’, views.UserViewSet)
router.register(r’groups’, views.GroupViewSet)
Wire up our API using automatic URL routing.
Additionally, we include login URLs for the browsable API.
urlpatterns = [
    url(r’^’, include(router.urls)),
]
1  | 试图  | 
from rest_framework import viewsets
from rest_framework import serializers
class UserSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = models.User
        fields = (‘url’, ‘username’, ‘email’, ‘groups’)
class GroupSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = models.Group
        fields = (‘url’, ‘name’)
class UserViewSet(viewsets.ModelViewSet):
    “””
    API endpoint that allows users to be viewed or edited.
    “””
    queryset = User.objects.all().order_by(‘-date_joined’)
    serializer_class = UserSerializer
class GroupViewSet(viewsets.ModelViewSet):
    “””
    API endpoint that allows groups to be viewed or edited.
    “””
    queryset = Group.objects.all()
    serializer_class = GroupSerializer
1  | 
  | 
from django.conf.urls import url, include
from web.views import s11_render
urlpatterns = [
    url(r’^test/$’, s11_render.TestView.as_view()),
    url(r’^test.(?P
]
1  | 试图:  | 
#!/usr/bin/env python
-- coding:utf-8 --
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers
from rest_framework.renderers import JSONRenderer
from .. import models
class TestSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.UserInfo
        fields = “all“
class TestView(APIView):
    renderer_classes = [JSONRenderer, ]
def get(self, request, *args, **kwargs):
    user_list = models.UserInfo.objects.all()
    ser = TestSerializer(instance=user_list, many=True)
    return Response(ser.data)1  | #### 3.10.2 表格  | 
#!/usr/bin/env python
-- coding:utf-8 --
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers
from rest_framework.renderers import AdminRenderer
from . import models
class TestSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.UserInfo
        fields = “all“
class TestView(APIView):
    renderer_classes = [AdminRenderer, ]
def get(self, request, *args, **kwargs):
    user_list = models.UserInfo.objects.all()
    ser = TestSerializer(instance=user_list, many=True)
    return Response(ser.data)1  | #### 3.10.3 Form表单  | 
#!/usr/bin/env python
-- coding:utf-8 --
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers
from rest_framework.renderers import JSONRenderer
from rest_framework.renderers import AdminRenderer
from rest_framework.renderers import HTMLFormRendere
from . import models
class TestSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.UserInfo
        fields = “all“
class TestView(APIView):
    renderer_classes = [HTMLFormRenderer, ]
def get(self, request, *args, **kwargs):
    user_list = models.UserInfo.objects.all().first()
    ser = TestSerializer(instance=user_list, many=False)
    return Response(ser.data)1  | #### 3.10.4 自定义显示模版  | 
from django.conf.urls import url, include
from web.views import s11_render
urlpatterns = [
    url(r’^test/$’, s11_render.TestView.as_view()),
    url(r’^test.(?P
]
1  | 试图  | 
#!/usr/bin/env python
-- coding:utf-8 --
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers
from rest_framework.renderers import TemplateHTMLRenderer
from . import models
class TestSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.UserInfo
        fields = “all“
class TestView(APIView):
    renderer_classes = [TemplateHTMLRenderer, ]
def get(self, request, *args, **kwargs):
    user_list = models.UserInfo.objects.all().first()
    ser = TestSerializer(instance=user_list, many=False)
    return Response(ser.data, template_name='user_detail.html')1  | 模版  | 
$ cat user_detail.html
1  | #### 3.10.5 浏览器API+JSON  | 
class TestSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.UserInfo
        fields = “all“
class CustomBrowsableAPIRenderer(BrowsableAPIRenderer):
    def get_default_renderer(self, view):
        return JSONRenderer()
class TestView(APIView):
    renderer_classes = [CustomBrowsableAPIRenderer, ]
def get(self, request, *args, **kwargs):
    user_list = models.UserInfo.objects.all().first()
    ser = TestSerializer(instance=user_list, many=False)
    return Response(ser.data, template_name='user_detail.html')注意: 如果同时多个存在时,自动根据URL后缀来选择渲染器
[原文地址](http://www.cnblogs.com/wupeiqi/articles/7805382.html)