基于Django实现 RESTful API 之RestFram
一、首先什么是RESTful
- REST与技术无关,代表的是一种软件架构风格,REST是Representational State Transfer的简称,中文翻译为“表征状态转移”
- REST从资源的角度类审视整个网络,它将分布在网络中某个节点的资源通过URL进行标识,客户端应用通过URL来获取资源的表征,获得这些表征致使这些应用转变状态
- 所有的数据,不管是通过网络获取的还是操作数据库获得(增删改查)的数据,都是资源,将一切数据视为资源是REST区别与其他架构风格的最本质属性
- 对于REST这种面向资源的架构风格,有人提出一种全新的结构理念,即:面向资源架构(ROA:Resource Oriented Architecture)
- 对互联网上的任意东西都可视为资源,他认为一个url就是一个资源 比如:http://www.xxx.com/get_user/
二、资源与URI
- REST全称是表述性状态转移,那究竟指的是什么的表述? 其实指的就是资源。任何事物,只要有被引用到的必要,它就是一个资源。
- 要让一个资源可以被识别,需要有个唯一标识,在Web中这个唯一标识就是URI(Uniform Resource Identifier)。
- URI既可以看成是资源的地址,也可以看成是资源的名称。如果某些信息没有使用URI来表示,那它就不能算是一个资源, 只能算是资源的一些信息而已。URI的设计应该遵循可寻址性原则,具有自描述性,需要在形式上给人以直觉上的关联。
URI设计上的一些技巧:
- 使用_或-来让URI可读性更好
- 使用/来表示资源的层级关系
- 使用?用来过滤资源
- ,或;可以用来表示同级资源的关系
三、什么是API
API就是接口,提供的url。接口有两个用途:
- 为别人提供服务
- 前后端分离,一个写vue,一个写后端,他们之间都是通过ajax请求
四、RESTful API设计
-
网络应用程序,分为前端和后端两个部分。当前的发展趋势,就是前端设备层出不穷(手机、平板、桌面电脑、其他专用设备......)。
-
因此,必须有一种统一的机制,方便不同的前端设备与后端进行通信。这导致API构架的流行,甚至出现"API First"的设计思想。RESTful API是目前比较成熟的一套互联网应用程序的API设计理论。
关于URL的设计大都长篇大论,这里我就不一一赘述了,通过一个简单的示例来说明,可能有不太符合的地,但是万变理解其宗就好。
- 首先:http协议请求方式:GET、POST、DELETE、PUT、PATCH、OPTION、HEAD、TRACE
- 之前URL的设计大多都是这种类型,不符合RESTful规范:
127.0.0.1:8000/books //查
127.0.0.1:8000/books/add //增
127.0.0.1:8000/books/change/1 //改
127.0.0.1:8000/books/delete/1 //删
- 符合RESTful规范的URL设计:
GET请求查看数据:
127.0.0.1:8000/books
返回所有数据列表 :[{}, {}, {}]
GET请求查看单条数据:
127.0.0.1:8000/books/1
返回查看的单条数据{}
POST请求添加数据:
127.0.0.1:8000/books
返回添加数据 :{}
PUT请求更新pk = 1的数据:
127.0.0.1:8000/books/1
返回更新后的数据: {}
Delete请求删除pk = 1的数据:
127.0.0.1:8000/books/1
返回空
五、基于Django实现API之RestFramework框架
- RestFramework框架:基于Django帮助我们快速开发符合RESTful规范的接口框架。
- Django实现的API许多功能都需要我们自己开发,未免太过麻烦,这时候Django restframework就给我们提供了方便,直接基于它来返回数据,总之原理都是一样的,就是给一个接口也就是url,让前端的人去请求这个url去获取数据,在页面上显示出来。这样也就达到了前后端分离的效果。下面我们来看看基于Django Rest Framework框架。
- 首先下载Django Rest Framework,
pip3 install djangorestframework
Rest Framework框架大体上分为以下十部分内容:
- (1) APIView
- (2) 解析器组件
- (3) 序列化组件
- (4) 视图类(mixin)
- (5) 认证组件
- (6) 权限组件
- (7) 频率组件
- (8) 分页组件
- (9) 响应器组件
- (10) url控制器
五|1 - APIView:
APIView的源码执行可以参考DjangoCBV的源码执行逻辑
一张图了解一下APIView的源码执行流程吧:
6_副本.png五|2- 解析器组件
对请求的数据进行解析:是针对请求体进行解析的。表示服务器可以解析的数据格式的种类
先来看Django中的发送请求:
- view.py中post请求的执行体:
2.png- 如果是这样的格式发送的数据,在POST里面有值
Content-Type: application/url-encoding..... //数据格式
request.body # 请求体中的原生数据
request.POST
- 如果是发送的json的格式,在POST里面是没有值的,在body里面有值,可通过decode,然后loads取值
Content-Type: application/json..... //数据格式
request.body # 请求体中的原生数据
request.POST
这种情况下每次都要decode(解码),loads(反序列化),真的很麻烦,所以才有的解析器组件。弥补了django的缺点。
接下来我们先了解restframework的解析器使用方法:
- 1、可以在CBV试图类中添加属性:
parser_classes = [JSONParser/FormParser/....] #表示服务器可以解析的数据格式的种类
- 2、可以在sittings配置项中添加(当然也可以在内置的settings文件中配置)django其实有两个settings文件:
需要用什么数据格式就添加什么数据格式
REST_FRAMEWORK={
'DEFAULT_PARSER_CLASSES': (
'rest_framework.parsers.JSONParser',
'rest_framework.parsers.FormParser',
),
}
-3、如果都没有配置的话那就使用默认的配置:
'DEFAULT_PARSER_CLASSES': (
'rest_framework.parsers.JSONParser',
'rest_framework.parsers.FormParser',
'rest_framework.parsers.MultiPartParser'
)
看到这可能就懵逼了,不过没关系,这几种方法其实都是因为分析了源码之后得出的,下面就一起来分析一下源码的执行流程吧:
步骤一、先来看看Django RestFramework的发送请求:(和Django的对比发现不同)
- view.py中post请求的执行体:
4.png- 如果是这样的格式发送的数据,在POST里面有值
Content-Type: application/url-encoding..... //数据格式
request.body # 请求体中的原生数据
request.POST
request.data #reqest.data是APIView重装request才有的,reqest.data取值的时候才执行解析器组件
- 如果是发送的json的格式,在POST里面是没有值的,在body里面有值,可通过decode,然后loads取值
Content-Type: application/json..... //数据格式
request.body # 请求体中的原生数据
request.POST
request.data #reqest.data是APIView重装request才有的,reqest.data取值的时候才执行解析器组件
步骤二、解析器源码执行流程:
解析器源码执行流程.jpg五|3- 序列化组件
- 首先表结构:models.py
from django.db import models
class Author(models.Model):
nid = models.AutoField(primary_key=True)
name = models.CharField(max_length=32)
age = models.IntegerField()
def __str__(self):
return self.name
class Publish(models.Model):
nid = models.AutoField(primary_key=True)
name = models.CharField(max_length=32)
city = models.CharField(max_length=32)
email = models.EmailField()
def __str__(self):
return self.name
class Book(models.Model):
nid = models.AutoField(primary_key=True)
title = models.CharField(max_length=32)
publishDate = models.DateField()
price = models.DecimalField(max_digits=5, decimal_places=2)
# 与Publish建立一对多的关系,外键字段建立在多的一方
publish = models.ForeignKey(to="Publish", to_field="nid", on_delete=models.CASCADE)
# 与Author表建立多对多的关系,ManyToManyField可以建在两个模型中的任意一个,自动创建第三张表
authors = models.ManyToManyField(to='Author', )
- 先看一下基于Django的CBV接口设计
缺点:需要自己设计数据格式、需要自己做序列化操作、需要自己讲不支持json序列化的数据类型转化
class Booklist(View):
def get(self, request):
book_obj = models.Book.objects.all()
ret = []
for obj in book_obj:
ret.append({
"title":obj.title,
"price":"%s"%obj.price,
})
return HttpResponse(json.dumps(ret,ensure_ascii=False))
- 基于Django的序列化组件的接口设计
缺点:只支持序列化,而不支持校验、错误提示等附加功能
from django.core.serializers import serialize
class Booklist(View):
def get(self, request):
book_obj = models.Book.objects.all()
data = serialize("json", book_obj)
return HttpResponse(data)
- 基于DRF ( Django RestFramework ) Serializer 的接口设计
DRF ( Django RestFramework ) Serializer的序列化方式可以类比Django的Form组件的使用
注意一点:因为Serializer和数据库没有实质上的联系,所以post请求时不能直接保存到数据库
from rest_framework.views import APIView
from app001 import models
# rest_framework重装的response
from rest_framework.response import Response
# 序列化组件的导入
from rest_framework import serializers
class BooklistSerializer(serializers.Serializer):
title = serializers.CharField(max_length=32)
price = serializers.DecimalField(max_digits=5, decimal_places=2)
publishDate = serializers.DateTimeField()
# 一对多字段,可以通过source参数取出想要的关联对象的任意字段
publish_name = serializers.CharField(source="publish.name")
publish_city = serializers.CharField(source="publish.city")
# 多对多字段,可以通过source参数取出所有关联对象queryset集合,几乎没用
# authors = serializers.CharField(max_length=32, source="authors.all")
# 多对多字段,固定的书写格式
authors = serializers.SerializerMethodField()
def get_authors(self, obj): # 函数命名必须是get_field形式,obj为当前字段对象
ret = []
for obj in obj.authors.all():
ret.append(obj.name)
return ret
class Booklist(APIView):
# 查 get
def get(self, request):
book_obj = models.Book.objects.all()
bs = BooklistSerializer(book_obj, many=True)
data = bs.data # 序列化接口
return Response(data)
#增 post
def post(self, request):
print(request.data) # 静态方法:解析数据工作
bs = BooklistSerializer(data=request.data)
if bs.is_valid(): # 校验
return Response(bs.data) # 序列化数据
else:
return Response(bs.errors) # 序列化错误信息
- 基于DRF ( Django RestFramework ) ModelSerializer 的接口设计
DRF ( Django RestFramework ) Serializer的序列化方式可以类比Django的ModelForm组件的使用
当涉及到一对多或者多对多字段时,我们可以通过自定制操作来获得我们想要的数据形式,
class BooklistSerializer(serializers.ModelSerializer):
class Meta:
model=models.Book
fields="__all__"
//一对多字段,可以通过source参数取出想要的关联对象的任意字段
//一对多字段,需要添加allow_blank=True参数,这个参数在post请求时有用
publish = serializers.CharField(source="publish.name",allow_blank=True)
publish_city = serializers.CharField(source="publish.city",allow_blank=True)
// 多对多字段,固定的书写格式
// 函数命名必须是get_field形式,obj为当前字段对象
authors = serializers.SerializerMethodField()
def get_authors(self, obj):
ret = []
for obj in obj.authors.all():
ret.append(obj.name)
return ret
class Booklist(APIView):
def get(self, request):
book_obj = models.Book.objects.all()
bs = BooklistSerializer(book_obj, many=True)
data = bs.data # 序列化接口
return Response(data)
def post(self, request):
// 静态方法:解析数据工作
print(request.data)
//post请求时不仅仅是序列化了,还需要验证、保存等,必须在实例化时加上data
//post发送的是添加请求,单个对象many=False
bs = BooklistSerializer(data=request.data,many=False)
// 校验
if bs.is_valid():
bs.save() // create操作
return Response(bs.data) // 序列化数据
else:
return Response(bs.errors) // 序列化错误信息