目录
- 一、简介
- 1、REST简介
- 2、API设计规范
- 二、基础操作
- 1.创建虚拟环境
- 2.安装软件
- 3.新建项目、app,并写入setting.py
- 三、序列化
- 1、创建一个model
- 2、使用Serializer创建一个序列化类
- 1)使用序列化类
- 3、使用ModelSerializers创建一个序列化类
一、简介
RestFramework是一个能快速为我们提供API接口,方便我们编程的框架。API是后端编程人员写的,为了让前端拿数据的一个接口,通常就是以url的形式存在。
在前后端分离的应用模式中,我们通常将后端开发的每个视图都成为一个接口,或者API,前端通过访问接口来对数据进行增删改查
我做的项目中前后端需要分离,那么Django能做的就完全是后端,不会用到render了。一般前后端交流就是:大部分情况前端会用ajax发送请求,后端人员则发送JSON字符串给前端,前端再反序列化后进行使用。这个时候其实我们设计一套API出来,就能使我们的任务轻松很多。所以,RestFramework就应运而生了。
1、REST简介
REST是一种组织Web服务的架构,作为架构,它提出了一系列架构级约束:
- 使用客户/服务器模型。客户和服务器之间通过一个统一的接口来互相通讯。
- 层次化的系统。在一个REST系统中,客户端并不会固定地与一个服务器打交道。
- 无状态。在一个REST系统中,服务端并不会保存有关客户的任何状态。也就是说,客户端自身负责用户状态的维持,并在每次发送请求时都需要提供足够的信息。
- 可缓存。REST系统需要能够恰当地缓存请求,以尽量减少服务端和客户端之间的信息传输,以提高性能。(比如在客户端请求一个自上次请求后没有发生过变化的信息时,如产品分类列表,服务端仅仅需要返回一个304响应即可。)
- 统一的接口。一个REST系统需要使用一个统一的接口来完成子系统之间以及服务与用户之间的交互。这使得REST系统中的各个子系统可以独自完成演化。
2、API设计规范
API并不是必须的,API的存在主要是为了解决多人开发,特别是前后端分离的情况。当前后端分离时,就不能用Django的render,后台只能通过接受前端的数据再将前端需要的数据发给它。
使用RestFramework设计API有一套规范,即RESTful:
- 域名:域名上要显示你使用了API,我们有两种方式
https://api.example.com 方式一:将API部署在专用域名上(是官网的推荐方式,但这么做会存在跨域问题)
https://example.org/api/ 方式二: 写在路径上,API很简单 - 版本:我们的项目在开发过程中会进行功能的添加及优化,这个时候我们通常会为每一个版本设定一个版本号,版本号的显示也有两种方式
https://api.example.com/v1/ 方式一: 写在路径上,API很简单
https://v1.example.com 方式二:将版本号部署在专用域名上(同样会存在跨域问题, 跨域时会引发发送多次请求) - 路径:视网络上任何东西都是资源,所以路径均使用名词表示(可复数)
https://api.example.com/v1/zoos https://api.example.com/v1/animals - 请求方式
GET :从服务器取出资源(一项或多项)
POST :在服务器新建一个资源
PUT :在服务器更新资源(客户端提供改变后的完整资源——全部修改)
PATCH :在服务器更新资源(客户端提供改变的属性——部分修改)
DELETE :从服务器删除资源 - 过滤:通过在URL上传参的方式,有GET请求获取相应的数据
https://api.example.com/v1/zoos?limit=10:指定返回数据的数量 https://api.example.com/v1/zoos?offset=10:指定返回数据的开始位置
https://api.example.com/v1/zoos?page=2&per_page=100:指定第几页以及每页的数据数量
https://api.example.com/v1/zoos?sortby=name&order=asc:指定返回结果按照哪个属性排序,以及排序顺序
https://api.example.com/v1/zoos?animal_type_id=1:指定筛选条件 - 状态码:我们可以通过状态码来判断请求的状态,以处理相应的请求。在状态码是4开头时,应该捕捉相应错误并返回错误信息
- 返回结果:针对不同操作,服务器向用户返回的结果应该符合以下规范。
GET/collection:返回资源对象的列表
GET/collection/resource:返回单个资源对象
POST/collection:返回新生成的资源对象
PUT/collection/resource:返回完整的资源对象
PATCH/collection/resource:返回完整的资源对象
DELETE/collection/resource:返回一个空文档 - Hypermedia API:RESTful API最好做到Hypermedia,即返回结果中提供链接,连向其他API方法,使得用户不查文档,也知道下一步应该做什么。
看到的其他的一些RESTful API实践:
- 使用名词而不是动词。比如使用GET代替read,使用Post代替create,使用Put代替update
- GET方法不能改变资源状态,如需改变应用PUT等方法
- 使用名词的复数形式而不是单数,为所有资源保持复数形式
- 为关系使用子资源
假如资源连接到其它资源,则使用子资源形式:
GET /cars/711/drivers/ Returns a list of drivers for car 711
GET /cars/711/drivers/4 Returns driver #4 for car 711
- 使用HTTP头决定序列化格式
在客户端和服务端都需要知道使用什么格式来进行通信,这个格式应该在HTTP头中指定:
Content-Type:定义请求的格式;
Accept :定义允许的响应格式的列表 - 使用HATEOAS
Hypermedia as the Engine of Application State是一个指导原则,它规定超文本链接应该被用于在API中创建更好的资源导航: - 为集合提供过滤、排序、字段选择以及分页
过滤
为所有字段或者查询语句提供独立的查询参数:
GET /cars?color=red Returns a list of red cars GET /cars?seats<=2
Returns a list of cars with a maximum of 2 seats
排序
允许跨越多字段的正序或者倒序排列:
GET /cars?sort=-manufactorer,+model
字段选择
一些情况下,我们只需要在列表中查询几个有标识意义的字段,我们不需要从服务端把所有字段的值都请求出来,所以需要支持API选择查询字段的能力,这也可以提到网络传输性能和速度:
GET /cars?fields=manufacturer,model,id,color
分页
使用offset和limit来获取固定数量的资源结果,当其中一个参数没有出现时,应该提供各自的默认值,比如默认取第一页,或者默认取20条数据:
GET /cars?offset=10&limit=5 GET /cars?&limit=5 //Get first five
result GET /cars?&offset=5 //Get default amount result offset 5
使用自定义的头X-Total-Count发回给调用段实际的资源数量。
前一页后一页的链接也应该在HTTP头链接中得到支持,遵从下文中的链接原则而不要构建你自己的头:
Link:
https://blog.mwaysolutions.com/sample/api/v1/cars?offset=15&limit=5;
rel=“next”,
https://blog.mwaysolutions.com/sample/api/v1/cars?offset=50&limit=3;
rel=“last”,
https://blog.mwaysolutions.com/sample/api/v1/cars?offset=0&limit=5;
rel=“first”,
https://blog.mwaysolutions.com/sample/api/v1/cars?offset=5&limit=5;
rel=“prev”,
- 版本化你的API
确保强制实行API版本,并且不要发布一个没有版本的API,使用简单的序列数字,避免使用2.5.0这样的形式:
/blog/api/v1 - 使用HTTP状态码处理错误
- 允许重写HTTP方法
一些代理只支持GET和POST方法,为了在这种限制下支持RESTful API,API需要重写HTTP方法。
使用自定义的X-HTTP-Method-Override HTTP头来重写POST方法。
二、基础操作
1.创建虚拟环境
在需要目录下cmd,Python3.3以上的版本通过venv模块原生支持虚拟环境,可以代替之前的virtualenv(就是可以不用pip安装一个新的包了,转了python就可以用)
python -m venv [虚拟环境名]
虚拟环境名\Scripts\activate
#退出虚拟环境
deactivate
2.安装软件
pip install django
pip install djangorestframework
#安装过慢解决方法
pip install -i 镜像源网站 django
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple django
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple djangorestframework
镜像源网站: 清华:https://pypi.tuna.tsinghua.edu.cn/simple
阿里云:http://mirrors.aliyun.com/pypi/simple/ 中国科技大学
https://pypi.mirrors.ustc.edu.cn/simple/ 华中理工大学:http://pypi.hustunique.com/ 山东理工大学:http://pypi.sdutlinux.org/
豆瓣:http://pypi.douban.com/simple/
tutorial/settings.py文件:
INSTALLED_APPS = (
...
'rest_framework',
)
3.新建项目、app,并写入setting.py
django-admin.py startproject tutorial
python manage.py startapp snippets
INSTALLED_APPS = (
...
'rest_framework',
'snippets',
)
三、序列化
1、创建一个model
这里命名为Snippet
2、使用Serializer创建一个序列化类
- 开发Web API的第一件事是为Web API提供一种将代码片段实例序列化和反序列化为诸如json之类的表示形式的方式。
- 使用与Django forms非常相似的序列化器(serializers)来实现
- app下创建serializers.py from
- from framework import serializers
class SnippetSerializer(serializers.Serializer):
id = serializers.IntegerField(read_only=True)
title = serializers.CharField(required=False, allow_blank=True, max_length=100)
#还有create和update方法
1)使用序列化类
实例序列化
from snippets.models import Snippet
from snippets.serializers import SnippetSerializer
from rest_framework.renderers import JSONRenderer
from rest_framework.parsers import JSONParser
snippet = Snippet(code='foo = "bar"\n')
snippet.save()
#实例序列化
serializer = SnippetSerializer(snippet)
serializer.data
# {'id': 2, 'title': u'', 'code': u'print "hello, world"\n', 'linenos': False, 'language': u'python', 'style': u'friendly'}
#序列化查询结果集,添加many=true
serializer = SnippetSerializer(Snippet.objects.all(), many=True)
serializer.data
# [OrderedDict([('id', 1), ('title', u''), ('code', u'foo = "bar"\n'), ('linenos', False), ('language', 'python'), ('style', 'friendly')]), OrderedDict([('id', 2), ('title', u''), ('code', u'print "hello, world"\n'), ('linenos', False), ('language', 'python'), ('style', 'friendly')]), OrderedDict([('id', 3), ('title', u''), ('code', u'print "hello, world"'), ('linenos', False), ('language', 'python'), ('style', 'friendly')])]
#将模型实例转换为Python原生数据类型。要完成序列化过程,我们将数据转换成json(之前我写的时转为比特流?)
content = JSONRenderer().render(serializer.data)
content
# '{"id": 2, "title": "", "code": "print \\"hello, world\\"\\n", "linenos": false, "language": "python", "style": "friendly"}'
反序列化
#首先将一个流(stream)解析为Python原生数据类型...
from django.utils.six import BytesIO
stream = BytesIO(content)
data = JSONParser().parse(stream)
#要将Python原生数据类型恢复成正常的对象实例
#下面应该是核查的过程,我之前写的是变成字典
serializer = SnippetSerializer(data=data)
serializer.is_valid()
# True
serializer.validated_data
# OrderedDict([('title', ''), ('code', 'print "hello, world"\n'), ('linenos', False), ('language', 'python'), ('style', 'friendly')])
serializer.save()
# <Snippet: Snippet object>
查看序列化器类实例的所有字段
from snippets.serializers import SnippetSerializer
serializer = SnippetSerializer()
print(repr(serializer))
# SnippetSerializer():
# id = IntegerField(label='ID', read_only=True)
# title = CharField(allow_blank=True, max_length=100, required=False)
# code = CharField(style={'base_template': 'textarea.html'})
# linenos = BooleanField(required=False)
# language = ChoiceField(choices=[('Clipper', 'FoxPro'), ('Cucumber', 'Gherkin'), ('RobotFramework', 'RobotFramework'), ('abap', 'ABAP'), ('ada', 'Ada')...
# style = ChoiceField(choices=[('autumn', 'autumn'), ('borland', 'borland'), ('bw', 'bw'), ('colorful', 'colorful')...
3、使用ModelSerializers创建一个序列化类
该类只是创建序列化器类的快捷方式,没有其他特别作用:
- 一组自动确定的字段。
- 默认简单实现的create()和update()方法。
class SnippetSerializer(serializers.ModelSerializer):
class Meta:
model = Snippet
fields = ('id', 'title', 'code', 'linenos', 'language', 'style')