一. 什么是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是当前字段对象
pass
class 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是当前字段对象
pass
class 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是当前字段对象
pass
class 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是当前字段对象
pass
class 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 = 1
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 response
1 | 访问验证: |
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 response
1 | 验证: |
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 response
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
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):
pass
1 | #### 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)