一:DRF入门
简介
这里是 我们 前后端分离 开发模式的开始-----> 后端的开始。
django-rest-framework:DRF,是Django的一个app
,可以更快地在Django框架上编写接口(不用也可以写符合规范的接口)。
DRF需要以下依赖:
- Python (2.7, 3.2, 3.3, 3.4, 3.5, 3.6)
- Django (1.10, 1.11, 2.0)
DRF是以Django扩展应用的方式提供的,所以我们可以直接利用已有的Django环境而无需从新创建。(若没有Django环境,需要先创建环境安装Django)
安装
pip3 install djangorestframework
需要注意的点:
在安装
djangorestframework
的时候,有时会自动把django升级到最新版本,如果需要指定的django版本,再安装即可
API接口
- 规定了前后台信息交互规则的url链接,也就是前后台信息交互的媒介。
为了在团队内部形成共识、防止个人习惯差异引起的混乱,我们需要找到一种大家都觉得很好的接口实现规范
,而且这种规范能够让后端写的接口,用途一目了然,减少双方之间的合作成本。
通过网络,规定了前后台信息交互规则的url链接,也就是前后台信息交互的媒介
Web API接口和一般的url链接还是有区别的,Web API接口简单概括有下面四大特点
- url:长得像返回数据的url链接
- 请求方式:get、post、put、patch、delete
- 采用get方式请求上方接口
- 请求参数:json或xml格式的key-value类型数据
- ak:6E823f587c95f0148c19993539b99295
- region:上海
- query:肯德基
- output:json
- 响应结果:json或xml格式的数据
# xml格式
https://api.map.baidu.com/place/v2/search?ak=6E823f587c95f0148c19993539b99295®ion=%E4%B8%8A%E6%B5%B7&query=%E8%82%AF%E5%BE%B7%E5%9F%BA&output=xml
#json格式
https://api.map.baidu.com/place/v2/search?ak=6E823f587c95f0148c19993539b99295®ion=%E4%B8%8A%E6%B5%B7&query=%E8%82%AF%E5%BE%B7%E5%9F%BA&output=json
{
"status":0,
"message":"ok",
"results":[
{
"name":"肯德基(罗餐厅)",
"location":{
"lat":31.415354,
"lng":121.357339
},
"address":"月罗路2380号",
"province":"上海市",
"city":"上海市",
"area":"宝山区",
"street_id":"339ed41ae1d6dc320a5cb37c",
"telephone":"(021)56761006",
"detail":1,
"uid":"339ed41ae1d6dc320a5cb37c"
}
...
]
}
目前市面上大部分公司开发人员使用的接口服务架构主要有:restful、rpc,soap。
rpc:翻译成中文:远程过程调用[远程服务调用]
优点:
不需要管当前操作是什么http请求,也不需要操作url地址的编写,对接简单
缺点:
接口多了,对应函数名和参数就多了,前端在请求api接口时,就会比较难找.容易出现重复的接口
把后端所有的数据/文件都看成资源
那么接口请求数据,本质上来说就是 对资源的操作 了
web项目中操作资源,无非就是增删查改,所以要求在地址栏中声明要操作的资源是什么,然后通过http请求动词来说明对资源进行哪一种操作
POST http://www.renran.cn/api/students/ 添加学生数据
GET http://www.renran.cn/api/students/ 获取所有学生
DELETE http://www.renran.cn/api/students/<pk>/ 删除id=pk的一个学生
PUT http://www.renran.cn/api/students/<pk>/ 修改一个学生的全部信息 [id,name,sex,age,]
PATCH http://www.renran.cn/api/students/<pk>/ 修改一个学生的部分信息[age]
优点:
维护开发简单,可以保证后期的开发不会出现太多重复接口
缺点:
有部分接口不会有明确的增删查改这种区分的,所以会出现一些不伦不类的接口。
会因为这些语义不明,不伦不类的接口导致后期的维护成本上升。
因为restful把对于资源的操作都理解成了增删查改,建议使用http,所以restful接口天生局限于web开发。
接口文档
- 可以手动写(公司有平台,录到平台里,)
- 自动生成(coreapi,swagger)
二:RESTful规范
简介
REST全称是Representational State Transfer
,中文意思是表述(编者注:通常译为表征性状态转移)。 它首次出现在2000年Roy Fielding的博士论文中。
RESTful是一种定义Web API接口的设计风格
,尤其适用于前后端分离
的应用模式中。
这种风格的理念认为后端开发任务就是提供数据的,对外提供的是数据资源的访问接口,所以在定义接口时,客户端访问的URL路径就表示这种要操作的数据资源。
而对于数据资源分别使用POST、DELETE、GET、UPDATE等请求动作来表达对数据的增删查改。
它与语言、平台无关,任何框架都可以写出符合RESTful规范的API接口
10条规范
1.数据的安全保障
URL链接一般都采用HTTP协议
进行传输
HTTPS协议 = HTTP协议 + SSL证书
用HTTPS协议
传输,可以提高
数据交互过程中的安全性
; API与用户的通信协议,总是使用HTTPs协议。
2.接口特征表现
URL中,有API的关键字
标识
例:
https://api.baidu.com/books/ # 尽量将API部署在专用域名(会存在跨域问题)
https://www.baidu.com/api/books/ # API很简单
3. 多数据版本共存
URL中 ,标识接口的版本
; 请求地址中带版本,或者在请求头中。
https://www.baidu.com/v1/books/
https://www.baidu.com/v2/books/
4.数据即资源,均使用名词(可以是复数)
- 接口一般都是前后台数据的交互,交互的数据 我们称之为
资源
- 一般提成使用资源的
复数形式
,不要使用动词
例:查询所有图书
正确示范:https://api.baidu.com/books/
错误示范:https://api.baidu.com/books/get_all_books
(不符合规范)
例:删除图书
正确示范:https://api.baidu.com/books/user
错误示范:https://api.baidu.com/books/delete-user
但是,
https://api.baidu.com/books/user
如何知道是查看还是删除图书呢?
5.由请求方式决定(区分)不同操作
-
https://api.baidu.com/books
---> get请求:获取所有书 -
https://api.baidu.com/books/1
---> get请求:获取主键为1的书 -
https://api.baidu.com/books
--->post请求:新增一本书 -
https://api.baidu.com/books/1
---> put请求:整体修改主键id为1的书 -
https://api.baidu.com/books/1
---> patch请求:局部修改主键为1的书 -
https://api.baidu.com/books/1
---> delete请求:删除主键为1的书
6.过滤,通过在url上传参的形式传递搜索条件
-
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
:指定筛选条件
7.响应状态码
这里的状态码不是之前的HTTP的响应状态码,这里是自定义的响应状态码
- http请求的状态码(2,3,4,5)
- 返回的数据,
携带状态码
(标志当次请求成功或失败)
data = {'code': 200}
8.错误处理,应返回错误信息
返回的数据中,需要携带错误信息
data = {'code': 401, 'error_msg': '客户端出错了'}
9.返回结果,应符合以下规范
针对不同的请求操作
,服务器向用户返回的结果应该符合以下规范
- GET /collection:返回资源对象的列表(数组)
[{},{},{}]
- GET /collection/resource:返回单个资源对象
{}
这是字典 - POST /collection:返回新生成的资源对象
{新增的书}
- PUT /collection/resource:返回完整的资源对象
{返回修改后的}
- PATCH /collection/resource:返回完整的资源对象
{返回修改后的}
;局部修改 - DELETE /collection/resource:返回一个空文档
单条数据:
{}
多条数据:
[]
新增数据:
返回新增的数据
修改数据:
返回修改的数据
删除数据:
返回空文档
以上是规范,实际企业是这么做的: {status:100,msg:查询成功,data:null}
10.需要url请求的资源 需要访问资源的请求链接
返回的数据中,携带链接地址
例:查询id为7的用户的信息
{
"code": 200,
"msg": "ok",
"results":[
{
"id": 7,
"name": "baobao",
"register_data": "2017-06-10 17:36:41",
"avatar": "https://image.baidu.com/Darker/07.jpg",
"url": "http://www.baobao.top"
}
...
]
}
接口测试工具:Postman
1 后端写好接口要测试,后端开发要使用一个工具测试接口(postman)
2 下载---一路下一步--装成功了
3 会发送http请求,get,post请求即可
4 请求地址带参数,请求体带数据,请求头加数据
5 响应cookie,响应头,响应体
Postman是一款接口调试工具,是一款免费的可视化软件,同时支持各种操作系统平台,是测试接口的首选工具。
Postman可以直接从官网:https://www.getpostman.com/downloads/下载获得,然后进行傻瓜式安装。
- 工作面板
- 简易的get请求
- 简易的post请求
- 案例:请求百度地图接口
三:DRF快速使用
使用DRF快速写 增删改查 的接口 开始咯;
源码中(csrf已经禁用掉了)
# 首先要在Django项目环境基础上操作,没有就创建Django项目;
# 3.1、安装DRF
- 前提是已经安装了django,建议安装在虚拟环境
# mkvirtualenv drfdemo -p python3
# pip install django
pip install djangorestframework
pip install pymysql
##3.1.1、创建django项目
cd ~/Desktop
django-admin startproject drfdemo
使用pycharm打开项目,设置虚拟环境的解析器,并修改manage.py中的后缀参数。
# 3.2、注册app
INSTALLED_APPS = [
'rest_framework'
]
# 3.3、创建模型操作类
class Student(models.Model):
# 模型字段
name = models.CharField(max_length=100,verbose_name="姓名")
sex = models.BooleanField(default=1,verbose_name="性别")
# 3.4、 为了方便测试,所以我们可以先创建一个数据库。
create database students charset=utf8;
#3.5、 数据库迁移
# 如果想用mysqal还需要配置DATABASES={} 和 指定设置默认的MySQLDB。
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': '数据库名',
'USER': '用户',
'PASSWORD': '密码',
'HOST': 'IP地址',
'PORT': '端口',
}
}
# 并且指定设置默认的MySQLDB,需要在项目文件夹或者应用文件夹内的__init__.py文件中书写固定的代码。
import pymysql
pymysql.install_as_MySQLdb()
# 3.6 路由
path('test/', views.Test.as_view()),
# 3.7 视图类views.py 定义视图的行为。
from rest_framework.views import APIView
from rest_framework.response import Response
class Test(APIView):
def get(self,request):
return Response({'name':'bao','age':'19'})
def post(self,request):
return Response({'name': 'you', 'age': '19'})
# 3.8 serializer.py序列化器
from rest_framework.serializers import ModelSerializer
from app import models
class BookSerializer(ModelSerializer): # 序列化器是用来定义API的表示形式。
class Meta:
model = models.Book
fields = '__all__'
# 在请求地址中访问
http://127.0.0.1:8001/test/
特点:
# 具体功能在具体模块下
from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.exceptions import APIException
from rest_framework.filters import OrderingFilter
from rest_framework.views import APIView
from rest_framework.pagination import PageNumberPagination
from rest_framework.settings import APISettings
# 自定义drf配置 - 在自己的settings.py
REST_FRAMEWORK = {
# 自定义修改drf的配置们
}
详细点的:
3.1 安装DRF
前提是已经安装了django,建议安装在虚拟环境
# mkvirtualenv drfdemo -p python3
# pip install django
pip install djangorestframework
pip install pymysql
3.1.1 创建django项目
cd ~/Desktop
django-admin startproject drfdemo
使用pycharm打开项目,设置虚拟环境的解析器,并修改manage.py中的后缀参数。(就是设置虚拟环境而已)
3.2 在setting的app中配置注册rest-framework
注册顺序没强制要求;在settings.py的INSTALLED_APPS中添加’rest_framework’。
INSTALLED_APPS = [
'rest_framework'
]
接下来就可以使用DRF提供的功能进行api接口开发了。在项目中如果使用rest_framework框架实现API接口,主要有以下三个步骤:
- 将请求的数据(如JSON格式)转换为模型类对象
- 操作数据库
- 将模型类对象转换为响应的数据(如JSON格式)
接下来,我们快速体验下我们搞懂drf以后的开发代码。接下来代码不需要理解,看步骤。
体验drf完全简写代码的过程(了解)
使用DRF快速写 增删改查 的接口; 开始咯;
为了方便测试,所以我们可以先创建一个数据库。
create database drf charset=utf8;
可能你还需要 初始化数据库连接
安装pymysql
pip install pymysql
主引用中__init__.py
设置使用pymysql作为数据库驱动
# 并且指定设置默认的MySQLDB,需要在项目文件夹或者应用文件夹内的__init__.py文件中书写固定的代码。
import pymysql
pymysql.install_as_MySQLdb()
settings.py配置文件中设置mysql的账号密码
# 如果想用mysqal还需要配置DATABASES={} 和 指定设置默认的MySQLDB。
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': '数据库名',
'USER': '用户',
'PASSWORD': '密码',
'HOST': 'IP地址',
'PORT': '端口',
}
}
3.3 创建模型操作类models.py
from django.db import models
# Create your models here.
class Book(models.Model):
name = models.CharField(max_length=18)
publish = models.CharField(max_length=32)
price = models.IntegerField()
3.4 执行数据迁移
python manage.py makemigrations
python manage.py migrate
如果中途报错 :
# 执行数据迁移 python manage.py makemigrations 报错如下:
解决方案:
注释掉 backends/mysql/base.py中的35和36行代码。
# 执行数据迁移发生以下错误:
解决方法:
backends/mysql/operations.py146行里面新增一个行代码:
好了回到正题!
记得在数据库中增加点书本数据哦!
3.5 编写视图views.py
from rest_framework.viewsets import ModelViewSet
from app import models
from app.serializer import BookSerializer
class BookView(ModelViewSet):
serializer_class =BookSerializer
queryset = models.Book.objects.all()
3.6 新建序列化器。
在app应用目录中新建serializers.py用于保存该应用的序列化器。
创建一个StudentModelSerializer用于序列化与反序列化。
serializer.py
# 创建序列化器类,回头会在试图中被调用
from rest_framework.serializers import ModelSerializer
from app import models
class BookSerializer(ModelSerializer):
class Meta:
model = models.Book
fields = '__all__'
- model 指明该序列化器处理的数据字段从模型类BookInfo参考生成
- fields 指明该序列化器包含模型类中的哪些字段,’all‘指明包含所有字段
3.7 urls.py
from rest_framework.routers import SimpleRouter
from app01 import views
router = SimpleRouter()
router.register('books', views.BookView)
urlpatterns = [
path('admin/', admin.site.urls),
]
urlpatterns += router.urls
接下来用浏览器或者postman工具测试,看! 发送GET请求就能查询到数据了,也可以指定id单独查某条数据。
POST增加数据:
这是再GET查询全部显示出来的书里就有新增的那本书了。
PACH修改数据:要指定数据的id
DELETE删除数据:删除单条数据要指定id 删除哈!、
再查询就没有了id为3的数据(南京纪念馆)
四:CBV和APIView源码分析
Django CBV 和drf CBV对比
Django CBV
- 继承了
View
视图类 - 通过
as_view()
来获取view函数地址 - 请求来了之后,调用view函数,内部调用dispatch函数完成请求的分发
- dispatch函数将请求方式映射成视图类的同名方法,完成请求的处理,得到相应、
- 最后将相应的结果一层层返回
drf CBV
- 继承了
APIView
类 - 通过
as_view()(继承自django的as_view)
获取view函数地址,但在view函数中局部禁用了csrf认证 - 请求来了调用view函数,内部调用(
APIView类的
)dispatch函数完成请求分发 - dispatch函数 二次封装request、进行三大认证后,再将请求方式映射成视图类的同名方法,完成请求的处理,得到相应,再对相应做渲染处理
- 最后将相应的结果一层层返回
4.1 APIView的as_view
- 内部还是执行了原生Django的
View
内部的闭包函数view
- 禁用了
csrf
- 一切皆对象,函数也是对象:
函数地址.name='Darker'
APIView的执行流程
# path('test/',APIView类的as_view内部是用了View的as_view内的view闭包函数),
'''
1 path的第二个参数是:APIView类的as_view内部是用了View的as_view内的view闭包函数
2 一旦有请求来了,匹配test路径成功
3 执行第二个参数view函数内存地址(requset),还是执行View的as_view内的view闭包函数,但是加了个csrf_exempt装饰器
4 所以,继承了APIView的所有接口,都没有csrf的校验了 (*****************)
5 执行self.dispatch(request)----》APIView类的
def dispatch(self, request, *args, **kwargs):
# 以后所有的request对象,都是****新的request对象***,它是drf的Request类的对象
request = self.initialize_request(request, *args, **kwargs)
self.request = request
try:
#整个drf的执行流程内的权限,频率,认证
self.initial(request, *args, **kwargs)
if request.method.lower() in self.http_method_names:
handler = getattr(self, request.method.lower(),
self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed
response = handler(request, *args, **kwargs)
except Exception as exc:
# 全局异常
response = self.handle_exception(exc)
# 响应
self.response = self.finalize_response(request, response, *args, **kwargs)
return self.response
'''
### request = self.initialize_request(request, *args, **kwargs)
##返回的request对象是drf Request类的request对象
def initialize_request(self, request, *args, **kwargs):
return Request(
request,
parsers=self.get_parsers(),
authenticators=self.get_authenticators(),
negotiator=self.get_content_negotiator(),
parser_context=parser_context
)
### *******以后,在视图类中使用的request对象已经不是原来的request对象了,现在都是drf的request对象了
#####你需要记住的
-0 所有的csrf都不校验了
-1 request对象变成了新的request对象,drf的request对象
-2 执行了权限,频率,认证
-3 捕获了全局异常(统一处理异常)
-4 处理了response对象,如果浏览器访问是一个样,postman访问又一个样
-5 以后,在视图类中使用的request对象已经不是原来的request对象了,现在都是drf的request对象了
补充
1 装饰器函数: csrf_exempt
#示例:
@csrf_exempt
def test():
pass
#本质就是,这种写法也是加装饰器的效果,但是很古怪,不过用在ur中到有点意思
test=csrf_exempt(test)
#看看urls.py中的
urlpatterns = [
path('admin/', admin.site.urls),
path('', 装饰器auth(views.index)), # 加了个auth认证装饰器,那么再views中就不需要@auth..这种写法啦!有点装X
]
4.2 cbv执行流程/原生View的as_view
- 本质上,执行了
self.dispatch(request, *args, **lwargs)
,执行的是APIView
的patch
path('test/',views.TestView.as_view()),
# path('test/',View类的as_view内部有个view闭包函数内存地址),
'''
1 path的第二个参数是:View类的as_view内部有个view闭包函数内存地址
2 一旦有请求来了,匹配test路径成功
3 执行第二个参数view函数内存地址(requset)
4 本质执行了self.dispatch(request)
5 通过反射去获得方法(如果是get请求,就是get方法)
if request.method.lower() in self.http_method_names:
handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
6 执行get方法,传入参数
handler(request, *args, **kwargs)
'''
3.dispatch源码分析
def dispatch(self, request, *args, **kwargs):
self.args = args
self.kwargs = kwargs
# DRF的Reauest对象,内部有request._request,是原生的request
request = self.initialize_request(request, *args, **kwargs)
# 现在的request是新的request,是drf的Request对象
self.request = request
try:
self.initial(request, *args, **kwargs)
if request.method.lower() in self.http_method_names:
handler = getattr(self, request.method.lower(),
self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed
response = handler(request, *args, **kwargs)
except Exception as exc:
response = self.handle_exception(exc)
self.response = self.finalize_response(request, response, *args, **kwargs)
return self.response
APIView的CBV源码分析详解
1.一开始进入根路径,根据路由找视图,执行了as_view()
,
相当于
path('', views.Index.as_view()(request))
2.但是Index
这个类里面并没有as_view()
,所以调用了Index
父类APIView
的as_view()
这里的APIView
来自rest_framework
的views
3.找到as_view()
4.调用了父类的as_view()
并且,禁用了csrf
:这个效果和 加装饰器局部禁用csrf 是一样的
5.这个父类的as_view()
就是原生Django的View
下的as_view()
所以,真正的程序在执行的时候,还是执行的原生Django的view
一切皆对象,函数也是对象:所以也可以通过.
的方法来取值和赋值
6.在原生Django的as_view
中,最后返回的是self.dispatch
这里的self
是视图里面定义的类,也就是Index
Index
继承的是APIView
,在APIView
中,有自己的dispatch
方法
7.这里request = self.initialize_request(request, *args, **kwargs)
的self
依旧是Index
但是在Index
中,并没有initialize_request
方法,所以就去父类APIView
中找
8.在APIView
中,确实存在initialize_request
9.将原始的request
传进来,最后实例化,并返回了1个Request
对象(DRF的Request
)
这里的返回的Request
对象,就是from rest_framework.response import Response
的Request
对象
四:Request类源码分析
1.Request类中
-
request._request
:原生request
-
request.data
:POST请求提交的数据(urlencoded、json、formdata) -
request.user
:不是原生的user了 -
request.query_params
:原生的request.GET
,遵循RESTful规范(6.查询参数,也就是GET请求获取的?后的参数
) - 重写了
__getattr__
、新的request.原来的所有属性和方法
:都可以直接用.
拿到
1 django 原生的Request:django.core.handlers.wsgi.WSGIRequest
2 drf的Request:rest_framework.request.Request
3 drf的request对象内有原生的request
request._request:原生的Request
4 在视图类中使用
request.method 拿到的就是请求方式,
正常拿,应该request._request.method
5 如何实现这种操作?
-对象.属性会触发 类的__getattr__方法
6 drf的Request类重写了__getattr__
def __getattr__(self, attr):
try:
# 去原生的request反射属性
return getattr(self._request, attr)
except AttributeError:
return self.__getattribute__(attr)
7 虽然视图类中request对象变成了drf的request,但是用起来,跟原来的一样,只不过它多了一些属性
-request.data #post请求提交的数据,不论什么格式,都在它中
-requst.query_params# get请求提交的数据(查询参数)
8 重点记住:
-drf的request对象用起来跟原来一样(重写了__getattr__)
-request.data #post请求提交的数据,不论什么格式,都在它中
-requst.query_params# get请求提交的数据(查询参数)
详解
1.在APIView
源码中,最后返回的是Request
对象
路径:rest_framework
- request.py
- Request
2.上面的request
是原生的,然后被传到了self._request
中,被当做内部变量
也就是说,之后的操作中:request._request
才是原来的request
3.上面的request
还是原生的,下面的request
是新的,是DRF的Request类
的对象
这个新的request
里面包含了原生request
是:request._request
(此处用到了面向对象的封装 )
4.此处的:self.initial(request, *args, **kwargs)
完成了权限
、频率
、认证
这里的request
已经是新的request
了
5.这里的inital
里面,就包括了:权限、频率、认证
self.perform_authentication(request) # 认证相关
self.check_permissions(request) # 权限相关
self.check_throttles(request) # 频率相关
6.这里的方法和之前原生django的dispatch
一样
发送什么请求,就会执行request.method
的请求方式的方法
8.此处可以打印request
(新的)和request._request
(原生的)的类型
DRF的request:
rest_framework.request.Request
原生的request:
django.core.handlers.wsgi.WSGIRequest
9.也可以打印request
(新的)和request._request
(原生的)的method
方法,看看发送的是什么请求
10.原生的request
和新的request
都有method
方法,说明它的内部肯定重写了__getattr__
方法
from rest_framework.request import Request
它在内部直接通过反射,去_request
(也就是原生request
)获取了需要的attr
也就是说,原来的request
对象的用法,这个新的request
都可以用
11.全局的异常捕获
这里会捕获一个全局的异常(例如:没有权限,超出访问评率,操作有误)
相当于在中间件的Exception
12.把视图函数(视图类)的response
进行了二次封装
1.内容有错还请在评论区指出哦!谢谢!