drf入门规范
DRF入门规范
DRF(django rest framework)是基于django的一个框架,可以帮助我们快速开发restful规范的的接口框架
DRF官方文档
后期学习资料
# 个人博客:https,证书过期,忽略
-https://www.liuqingzheng.top/
# cnblogs博客(后期迁移到这里,高级部分的内容)
-https://www.cnblogs.com/liuqingzheng
# 知乎
-https://www.zhihu.com/people/justin_liu
drf整体内容
1.入门规范
web开发模式
api接口
接口测试工具
restful规范
序列化反序列化
drf快速使用
APIView,Request类
drf把django的好多东西都重写了
2.序列化组件(重点)
基本序列化类
模型类序列化类
如何序列化
如何反序列化
反序列化的数据校验功能
3.请求与响应
drf的request已经不是原来django的request了
drf的reponse已经不是原来django的reponse了
指定可以接收请求的编码方式
指定响应格式
4.视图组件(重点) View
两个视图基类
5个视图拓展类
9个视图子类
视图集
5.路由
自动生成路由
6.认证(登录认证)、权限、频率(重点)
7.过滤、排序、分页(次重点)
8.手动、自动生成接口文档
9.JWT登录认证的,区别于cookie和session(目前是主流)
10.后台管理美化(django原生,simpleui二次开发)
11.基于角色的访问控制、权限的控制
web 应用模式
django web框架,其实用来写web项目,之前学习的BBS项目,图书管理系统都是用的前后端混合开发的项目,今天开始学习的是前后端分离的项目
前后端混合开发项目
-
后端人员,既要写后端代码也要写前端的模板语法【xxx.html】,也就是xxx.html的python代码
-
全栈开发,是前后端混合时代


前后端分离项目
-
后端人员只需要写后端逻辑和接口,前端页面渲染有前端人员负责
-
全栈开发,web后端,前端框架(vue,react)


API接口
概念
API(Application Programming Interface,应用程序编程接口)是一些预先定义的函数,目的是提供应用程序与开发人员基于某软件或硬件的以访问一组例程的能力,而又无需访问源码,或理解内部工作机制的细节。API除了有应用“应用程序接口”的意思外,还特指 API的说明文档,也称为帮助文档。
API:应用程序接口(API:Application Program Interface)
应用程序接口是一组定义、程序及协议的集合,通过 API 接口实现计算机软件之间的相互通信。API 的一个主要功能是提供通用功能集。程序员通过调用 API 函数对应用程序进行开发,可以减轻编程任务。 API 同时也是一种中间件,为各种不同平台提供数据共享。
简单理解API
防止因个人习惯差异引起的混乱接口,找到并形成团队有共识、统一的接口规范,而且这种规范能够让后端写的接口,用途一目了然,减少双方之间的合作成本(前后端)
通过网络,规定了前后台信息交互规则的url链接,也就是前后台信息交互的媒介
http://127.0.0.1/book/
点赞、点踩 就是API接口
API接口:前后端交互的媒介
前端指的是大前端,不仅仅是网站了,还有app/小程序;后端:python/go/java....的web后端,目前使用的是python的django写后端
url地址:https://api.map.baidu.com/place/v2/search
请求方式:get post delete put .....
请求参数:json格式的key-value类型数据
# 请求数据:地址栏中数据--》request.GET,请求体中数据:requsest.POST
早些年 前后端交互使用xml格式 ajax其实就是异步JavaScript和XML
后来 随着json格式的出现,乃至今都是主流
未来 更高效、安全的交互格式会替代目前的Json格式
响应结果:json格式的数据
API接口案例
https://api.map.baidu.com/place/v2/search?ak=6E823f587c95f0148c19993539b99295®ion=上海&query=肯德基&output=xml
https://api.map.baidu.com/place/v2/search?ak=6E823f587c95f0148c19993539b99295®ion=上海&query=肯德基&output=json
xml与json格式的区别?
JSON是JavaScript Object Notation;XML是可扩展标记语言JSON是基于JavaScript语言;XML源自SGML。JSON是一种表示对象的方式;XML是一种标记语言,使用标记结构来表示数据项。JSON不提供对命名空间的任何支持;XML支持名称空间。JSON支持数组;XML不支持数组。XML的文件相对难以阅读和解释;与XML相比,JSON的文件非常易于阅读。JSON不使用结束标记;XML有开始和结束标签。JSON的安全性较低;XML比JSON更安全。JSON不支持注释;XML支持注释。JSON仅支持UTF-8编码;XML支持各种编码
前端、前台、后端及后台的区别
前后端是指代码上的,前后台是指用户视觉上的。

接口测试工具postman
后端写好了接口,测试通过接口测完才能上线。引入接口测试软件工具postman
测试工具有:
浏览器只能发送`get`请求,不能自动发送`post/delete`请求
1.`postman`:开始是开源软件,只是谷歌浏览器的插件,后面越做越好,后面就可以安装到操作系统,后来就收费了
2.`postwoman`
3.其他的,不同公司可能不一样
注意:测试工具只是用来发送http请求
postman下载
官方下载:https://www.postman.com/downloads/?utm_source=postman-home
软件:Postman-win64-Setup.exe
使用:双击即可
postman基本使用
# 请求体的编码格式
urlencoded:默认情况--->key value形式--->组成成name=kiki&age=18 放到了请求体中request.boby
form-data:传文件和数据,组装成很复杂形式,暂时先不讲,从request.body中一取就报错
json格式:放在body体中{"name":"kiki","age":19}
# 返回数据
body:响应体
如果是浏览器就是看到的页面;
如果是postman就是下面第三张图片显示的形式
响应头
响应的cookie



restful规范
概念
REST全称是Representational State Transfer,中文意思是表述:表征性状态转移,它首次出现在2000年Roy Fielding的博士论文中。
Restful是一种定义Web API接口的设计风格,尤其使用于前后端分离的应用模式中
restful 10规范
-
数据的安全保障,通常使用
https(http+ssl/tsl)协议url链接一般都采用https协议进行传输采用https协议,可以提高数据交互过程中的安全性
-
接口中带
api标识https://api.kiki.com/books https://www.kiki.com/api/books 常用接口形式 -
多版本共存,路径中带版本信息
https://api.kiki.com/v1/login https://api.kiki.com/api/v2/login -
数据即是资源,均使用名词,尽量不出现动词(最核心的)
接口一般都是完成前后台数据的交互,交互的数据我们称之为资源
接口形式如下:
https://api.baidu.com/users https://api.baidu.com/books特殊的接口可以出现动词,因为这些接口一般没有一个明确的资源,或是动词就是接口的核心含义
https://api.baidu.com/login -
资源操作由请求方式决定(method)
操作资源一般都会涉及到增删改查,提供请求方式来标识增删改查动作
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请求:修改主键为1的书 https://api.baidu.com/books/1 delete请求:删除主键为1的书 -
在请求地址中带过滤条件
https://api.baidu.com/books?name=kiki&price=99 -
响应中状态码:两套
http响应状态码:1xx:请求正在处理,2xx:成功响应,3xx:重定向,4xx:客户端错误,5xx:服务端错误 状态码详细信息:https://blog.csdn.net/li_chunlong/article/details/120787872 公司内部规定的响应状态码,放在响应体中 {code:0} 后期一般使用100 101 102 这种状态码 -
返回数据中带错误信息
{ code:0 mag:"ok/用户名错误" } -
返回的结果应该符合一下规范 ---好多公司不遵循这个
GET 获取所有数据:返回资源对象的列表(数组)[{"name":"红楼梦","price":99},{"name":"红楼梦","price":99},{"name":"红楼梦","price":99}] GET 单个对象:返回单个资源对象:{"name":"红楼梦","price":99} POST 新增对象:返回新生成的资源对象:{"name":"西游记","price":99} PUT 修改对象:返回完整的资源对象:{"name":"西游记","price":100} DELETE 删除:返回一个空文档 -
响应数据中带链接
序列化反序列化
api接口开发,最核心最常见的一个过程就是序列化,所谓序列化就是把[数据转换格式],序列化可以分为两个阶段:
序列化
把程序员识别的数据转换成指定的格式提供给别人。
字典、列表以json格式存进文件中
举例:在django中获取到的数据默认是模型对象,但模型对象数据无法直接提供给前端或别的平台使用,所以需要把数据进行序列化,变成字符串或者json数据,提供给别人
read:读取数据库信息>>>序列化的过程
反序列化
把别人提供的数据转换/还原成程序员需要的格式
举例:前端js提供过来的json数据,对于python而已就是字符串,需要进行反序列化成模型类对象,通过os模块将数据保存到数据库中
write:将别人的数据转换后写入数据中>>>:反序列化的过程
基于django原生编写5个接口
# 以后写的接口,基本都是5个接口及其变形
查询所有:JsonResponse HttpResponse
查询单个:根据pk查到单个对象,直接序列化后返回给前端
新增一个:request.POST 取出前端传入body的数据,只能取出form-data,urlencoded,不能取出josn格式,request.body --->json--->反序列化成字典,再用(name=lqz&age=19)
修改一个:put请求放到body中的数据,在request.POST取不出来,只能自己从request.body中取出来
删除一个:通过pk直接删除
# 基于books单表为例,写5个接口
创建book表
表迁移
录入表的数据:直接录入/后台管理录入
写查询所有接口 遵循restful规范,使用cbv
新增一个数据 post()
查询一个数据 get()
修改一个数据:put提交的数据,不能从requets.POST中提取,>>>request.body
删除一个数据 delete()
- 创建表book

- 表迁移、录入表的数据:直接录入/后台管理录入

-
写查询所有接口 遵循restful规范,使用cbv
用到get请求,注释crsf第四个中间件
from django.views import View from .models import Book from django.http import JsonResponse class BookView(View): # 查询所有的图书 def get(self,request): # 查询出所有的图书,queryset对象,不能直接给前端 books = Book.objects.all() # 转成json格式后传给前端 # 把queryset对象转成列表,然后再使用JsonResponse book_list = [] for book in books: book_list.append({'name':book.name,'price':book.price,'publish':book.publish}) # 拓展知识 # return HttpResponse(json.dumps(book_list,ensure_ascii=False)) # 指定ensure_ascii=false,前端页面就显示中文了 """ 1.safe=False 'In order to allow non-dict objects to be serialized set the ''safe parameter to False.' book_list是列表,想要序列化,要把safe=False 2.底层函数 def __init__(self, data, encoder=DjangoJSONEncoder, safe=True, json_dumps_params=None, **kwargs): if safe and not isinstance(data, dict): raise TypeError( 'In order to allow non-dict objects to be serialized set the ' 'safe parameter to False.' ) if json_dumps_params is None: json_dumps_params = {} kwargs.setdefault('content_type', 'application/json') data = json.dumps(data, cls=encoder, **json_dumps_params) super().__init__(content=data, **kwargs) 3.底层数据是把数据打散后传入的 data = json.dumps(data, cls=encoder, **json_dumps_params) """ return JsonResponse(book_list,safe=False,json_dumps_params={'ensure_ascii':False}) # JsonResponse一般传字典和列表,可以通过查看源码可知json格式支持传数据类型前端展示,注意
safe=False和json_dumps_params={'ensure_ascii':False}

-
新增一个数据
新增一个(只能使用urlencoded或form-data编码,使用json形式编码不行,因为json格式编码提交的数据,不能从request.POST中取,从body中)
def post(self,request):
# 取出前端传入的数据
name = request.POST.get('name')
price = request.POST.get('price')
publish = request.POST.get('publish')
# 存到数据库中
book = Book.objects.create(name=name,price=price,publish=publish)
return JsonResponse({'name':book.name,'price':book.price,'publish':book.publish})
!
使用json格式的话,响应结果都是none,因为json格式编码提交的数据,不能从request.POST中取,从body中)
-
查询一个数据
路由不一样,需要新写一个路由,可用转换器或者正则表达式,但使用转换器较多
# 获取图书,修改图书,删除图书 path('api/v1/books/<int:pk>',views.BookView.as_view()), # re_path('api/v1/books/(?P<pk>\d+)',views.BookView.as_view())因为获取单条数据也是get请求,第一个CBV类已有一个get请求的情况下,需要再开一个CBV类
class BOokDetailView(View): # 获取单条 def get(self, request, pk): book = Book.objects.filter(pk=pk).first() # book 模型对象转成字典,使用JsonResponse返回 return JsonResponse({'id': book.pk, 'name': book.name, 'price': book.price, 'publish': book.publish}) -
修改一个数据
request.POST只能取post提交的urlencoded或form-data编码数据,put提交的取不到def put(self,request,pk): # 查询要改的 book = Book.objects.filter(pk=pk).first() # 取出前端传入的数据,修改完保存,>>>存在问题,因为put提交的取不到 # book.name=request.POST.get('name') # put请求取不到name # book.price=request.POST.get('price') # book.publish=request.POST.get('publish') # book.save() # 保存 """request.POST只能取post提交的urlencoded或form-data编码数据, put提交的取不到 """ # 前端使用json格式提交,自己保存 print(request.body) book_dict = json.loads(request.body) book.name = book_dict.get('name') book.price = book_dict.get('price') book.publish = book_dict.get('publish') book.save() return JsonResponse({'id': book.pk, 'name': book.name, 'price': book.price, 'publish': book.publish}) -
删除一个数据
def delete(self,request,pk): Book.objects.filter(pk=pk).delete() return JsonResponse(data={})
drf介绍和快速使用
djangorestframework drf 帮助快速实现符合restful规范的接口,目前django 最新版本是4.x ,一般都会用最新版的上一版3.x,drf最新版支持到django 3.x ,最新版不支持django 2.x
drf介绍
- djagno的一个app(组件),最新版本 3.x---》支持djagno 3.x以上
- 框架,模块版本不一致导致程序运行不起来
- 方便我们快速的在django框架上编写符合restful规范的接口
drf的下载与安装
pip3.8 install djangorestframework -i http://pypi.douban.com/simple/ --trusted-host pypi.douban.com/simple/
注意:目前下载的django是 2.2.2 版本,下载 drf会发现django版本太低不支持,下载drf会自动安装最新版django 4.x;如果下载的是django 3.x,下载drf不会有任何影响
drf编写5个接口(了解)
# views中
from .serializer import BookSerializer
from rest_frnamework.viewsets import ModelViewSet
class BookView(ModelViewSet):
queryset = Book.objects.all()
serializer_class = BookSerializer
# serializer
from rest_framework import serializers
from .models import Book
class BookSerializer(serializer.ModelSerializer):
class Meta:
model = Book
fields = '__all__'
# urls中
from rest_framework.routers import SimpleRouter
router = SimpleRouter()
router.register('book',views.BookView,'books')
urlpatterns = [
path('admin/',admin.site.urls),
]
# 两个列表相加 [1,2,4]+[6,7,8]
urlpatterns += router.urls
cbv源码分析
(一)
路由:path('api/v1/books/', views.BookView.as_view()),# 第二个参数无论是fbv还是cbv放的都是函数内存地址
1.当请求来了,匹配成功会执行 views.BookView.as_view()(request)
2.views.BookView.as_view()执行结果是View的类方法as_view,返回的结果是内层函数view,是个函数内层地址
3.本身请求来了,匹配成功,会执行view(request)
#1.第一步
def view(request, *args, **kwargs):
return self.dispatch(request, *args, **kwargs)
# 2.第二步 self.dispatch
def dispatch(self, request, *args, **kwargs):
# 3.request.method请求方式转成小写,必须在列表中才能往下走
if request.method.lower() in self.http_method_names:
# 4.反射,去self【视图类的对象:BookView】,去通过get字符串,反射出属性或方法
# 5.BookView的get方法
handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed
# 5.BookView的get方法,加括号,传入request
return handler(request, *args, **kwargs)
(二)
路由中:path('api/v1/books/', views.BookView.as_view())---》路由匹配成功就会执行---》views.BookView.as_view()(request)---->看一下BookView.as_view是怎么写的---》View这个类中的as_view类的绑定方法---》执行结果是view的内存地址[as_view中的内存函数,闭包函数]---》本质请求来了,执行view(request)--->return self.dispatch(request, *args, **kwargs)--->对象,谁的对象,View的对象,咱们从BookView---》View---》self是BookView的对象----》发现BookView没有dispatch---》View类的dispatch
def dispatch(self, request, *args, **kwargs):
if request.method.lower() in self.http_method_names:
# self是BookView的对象,通过字符串get找方法或属性,找到了get方法
#handler就是BookView类的get方法
handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed
# 执行 BookView的get(request)---->get请求就会执行BookView的get方法,post请求就会执行BookView的post方法
return handler(request, *args, **kwargs)
APIView执行内容(难)
-
基于
APIView + JsonResponse编写接口
APIView是基于django原生的View编写接口的,是drf提供给一个类(APIView),使用drf编写视图类,都是继承这个类及子类,APIView本身就是继承了Django原生的Viewfrom rest_framework.views import APIView #导入APIView from .models import Book from django.http import JsonResponse class BookView(APIView): def get(self,request): books=Book.objects.all() book_list=[] for book in books: book_list.append({'name':book.name,'price':book.price,'publish':book.publish}) return JsonResponse(book_list,safe=False)json序列化列表,
book_list是列表,想要序列化,需要将safe=False, -
基于APIView + Response 写接口
from rest_framework.views import APIView,Response from .models import Book class BookView(APIView): def get(self,request): books=Book.objects.all() book_list=[] for book in books: book_list.append({'name':book.name,'price':book.price,'publish':book.publish}) return Response(book_list) # 无论是列表还是字典都可以序列化 -
APIView的执行流程
分析
路由中写的:
path('books/', views.BookView.as_view()),-->>请求来了,执行
views.BookView.as_view()()--->现在的as_view是APIView的as_view# APIView的as_view方法:view还是原来的view,但是以后再也没有csrf认证了 @classmehtod def as_view(cls,**initkwargs): # 调用父类的as_view,父类是django原生的View # 把django原生View的as_view方法中的闭包函数view拿出来了 view = super().as_view(**initkwargs) # csrf_exempt排除所有的csrf的认证 # 相当于在所有的方法上加上了这个装饰器 return csrf_exempt(view) # 路由匹配成功,执行csrf_exempt(view)(request) -->View的as_view中的闭包函数view -->self.dispatch --->self(父类view方法产生的`self = cls(**initkwargs)`)是视图类的对象(BookView)产生 -->APIView的dispatch def dispatch(self,request,*args,**kwargs): #APIView的dispatch # request是django原生的request,原生request # 把原生request包装成新的request,这是drf提供的Request类的对象 request= self.initialize_request(request,*args,**kwargs) # 从这里开始,出现的request都是drf提供的Request类的对象 # 原生request是在request._request里面 # 把新生的request放到了self对象【BookView的对象】 self.request = request try: # 执行了三大认证【认证/频率/权限】,使用新的request,不读 self.initial(request,*args,**kwargs) #反射出视图类【BookView】里面的方法 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 # 把新的request传入了,视图类的方法中get的request也是新的 response = handler(request, *args, **kwargs) except Exception as exc: # 在执行3大认证和视图类种方法的过程中,如果处理异常,都能捕获到(异常捕获是全局异常捕获) response = self.handle_exeception(exc) self.response = self.finalize_response(request,response,*args,**kwargs) return self.response总结
APIView的执行流程:
- 排除了所有
csrf的认证 - 将
django中的原生request包装成drf提供的Request类 新的request,视图类中的request和Request类的对象都是包装后的request。原生的request在新的request._request中 - 在执行视图类的方法之前,执行了3大认证
- 如果在三大认证或者视图函数方法执行过程中出了错,会有异常捕获(异常捕获是全局异常捕获)
- 以后视图类方法中的
request都是新生的request
- 排除了所有
补充知识
装饰器的本质
# 装饰器
def auth(func):
def outer(*args,**kwargs):
res= func(*args,**kwargs)
return res
return outer
@auth # 本质add=auth(add)
def add(): # 函数
pass
# 使用auth装饰器装饰add函数,以后再使用add,其实就是在使用auth(add)的返回结果
Request对象源码分析(难)
区分原生request和新生request
原生request:django.core.handlers.wsgi.WSGIRequest
新生request:from rest_framework.request import Request
原生request可以在新生request._request中获取
Request源码
# 方法
1.__getattr__
在视图类的方法中,执行request.method,新生的request是没有method的,就触发了新的Request的__getattr__方法的执行
def __getattr__(self, attr):
try:
# 从老的request中反射出 要取得属性
return getattr(self._request, attr)
except AttributeError:
return self.__getattribute__(attr)
2.request.data 包装了数据属性
无论是post/put请求,放在了body中提交的数据,都是从request.data中取,取出来就是字典
所有编码格式都可以
3.request.query_params 包装了数据属性
get请求携带的参数,以后从这里取
query_params:查询参数 ----->restful规范请求地址中带查询参数
4.request.FILES 包装了数据属性
前端提交的文件,从request.FILES 取
Request类总结
- 新生
request跟之前用法一模一样,因为新的request取不到,会取原生request的__getattr__ request.data无论什么编码,什么请求方式,只要是body中的数据,就从这里取出-->字典request.query_params就是原来的request._request.GET- 上传的文件从
request.FILES
复习魔法方法
# 在类中,某种情况下会触发,会自动执行魔法方法
__str__:打印对象会调用
__init__:类()会调用
__call__:对象()会调用
__getattr__:对象点属性,如果属性不存在就会触发它的执行
前端不同的编码格式后端打印
urlencoded: name=lqz&age=19&price=999 放在了请求体中
formdata: 分数据部分和文件部分
'-------------------585520151165741599946333\r\n
Content-Disposition: form-data;
name="name"\r\n\r\nlqz\r\n
------------------585520151165741599946333--\r\n'
json格式: {"name":"lqz","age":19}
作业
# 1 使用ajax提交到后台数据
ajax urlencoded
form-data
json
后台直接打印
djang request.body
1.urlencoded
$('#subBtn').click(function (){
$.ajax({
url:'',
type:'post',
date:{'username':$('#name').val(),'password':$('#password').val(),'csrfmiddlewaretoken':'{{ csrf_token }}'},
success:function (args){
}
})
})
2.form-data
添加表头
<form action="" method="post" enctype="multipart/form-data">
<script>
$('#subBtn').click(function (){
$.ajax({
url:'',
type:'post',
date:{'username':$('#name').val(),'password':$('#password').val(),'csrfmiddlewaretoken':'{{ csrf_token }}'},
contentType:false,
processData:false,// 使用form_data交互
success:function (args){
}
})
})
</script>
3.json
$('#d1').click(function)(){
$.ajax({
url:'',
type:'post',
data:JSON.stringify({'name':'kimi','age':18}),//千万要写真实的数据
contentType:'application/json',
success:funcion (args){
alert(args)
}
})
}
</script>
问题总结:
1.使用pycharm打开sqlite的问题
sqlite文件,文件形式可能是文本模式或是其他形式(xx.mp4,xx.txt),但是他们的本质是一样的,只是打开的方式/编辑器不同而呈现不同的效果。
如果我们首次打开它选择了txt,之后打开的都是txt,如何解决?
1.使用navicate编辑器打开并编辑,不用pycharm编辑器
2.使用pycharm正常连接sqlite(同链接mysql)
2.模型表的改动都需要重新迁移数据,但如果改动了模型的表名,迁移了数据库,但是sqlite数据库的表名还是没变,如何操作?
1.删除了数据库sqlite
2.在app01下的文件migrations下的initial.py文件,如下图

drf的思维导图


浙公网安备 33010602011771号