web应用模式/api接口/postman安装(界面简介)/drf安装使用
内容概要
- drf整体知识点
1入门规范
- web应用模式
- api接口
- postman 使用教程
- restful规范
- 序列化反序列化
- 基于django原生框架编写5个接口
- drf介绍及快速使用
- cbv源码分析
2序列化组件
- 基本序列化类
- 模型类序列化
- 如何序列化/反序列化
- 反序列化的数据校验功能
3请求和响应
- drf的request(不同于django原生request)
- drf的response(不同于django原生response)
- 指定接收请求的编码格式
- 指定发出响应的编码格式
4视图组件
- 视图组件(2个基类,5个视图拓展类,9个是视图子类,视图集)
5路由
- 自动生成路由
6认证(登录认证),权限,频率
7过滤,排序,分页
8自动生成接口文档
扩展知识点
9JWT登录认证,区别与cookie和session
10后端管理美化(simpleui二次开发)
11基于角色的访问控制(权限控制)
web应用模式
前后端结合
# django 是针对web的框架 专门用来编写web项目的
在此之前我们编写的bbs项目,图书管理系统使用的是 前后端混合开发
相当于我们既要编写前端页面代码也要编写后端处理数据逻辑代码
主要是在前端页面编写的时候需要编写python代码(模板语法)
--> 相当于全栈开发 --> 在前后端混合时代比较多
前后端分离
后端人员,不需要接触前端,只需要编写接口即可
全栈开发>>>>web后端+前端框架(vue,react)
API接口
# 为了在团队内部形成共识、防止个人习惯差异引起的混乱,我们需要找到一种大家都觉得很好的接口实现规范,而且这种规范能够让后端写的接口,用途一目了然,减少双方之间的合作成本【前后端】
# 通过网络,规定了前后台信息交互规则的url链接,也就是前后台信息交互的媒介
# api接口:就是前后端交互的媒介
-url地址:https://api.map.baidu.com/place/v2/search
-请求方式:get,post,delete,put。。。。
-请求参数:json格式的key-value类型数据
-早些年 前后端交互使用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
我们分别使用不同的output方式 访问 api接口 返回的不一样类型的数据格式
在图中我们就可以看到区别
Postman使用教程
# 作为后端,写好了接口,我们自己需要先测试通过,再给别人用
# 写的接口,需要有个工具测试
-浏览器只能发送get请求,不能自动发送post,delete请求
所以需要使用以下工具来辅助解决
-postman
-postwoman
-这个工具只是用来发送http请求!
postman下载安装
https://www.postman.com/downloads/?utm_source=postman-home
下载好后直接双击安装即可
界面简介
restful规范
# 1.restful概念
REST全称是Representational State Transfer,中文意思是表述:表征性状态转移,它首次出现在2000年Roy Fielding的博士论文中。
RESTful是一种定义Web API接口的设计风格,尤其适用于前后端分离的应用模式中
2.10条规范
1.数据的安全保障.我通常都是使用 https(http+ssl/tsl) 协议
url连接一般都采用Https协议进行传输
采用https协议传输的好处就是>>> 可以提高数据交互过程中的安全性
2.接口中带api标识
'地址中带api标识'
http://api.wunee.cn/
'路径中带api标识'
http://wunee.cn/api/book >>> 主要使用这种格式
3.多版本共存>>路径中携带版本信息
我们在编写项目时可能需要进行更新操作,那么我们之前的项目版本可能还会在进行使用
且用户不能全部强制更新,这样我们就需要对多版本项目进行共存,以免对用户体验造成影响
http://api.wunee.cn/version1/login >>>版本1
http://api.wunee.cn/version2/login >>>版本2
4.数据即为资源,均使用名词,尽量不要出现动词(核心)
接口一般都是完成前端后端数据的交互,交互的数据我们称之为资源
接口形式:
http://api.baidu.com/users/ >>> 用户相关接口
http://api.baidu.com/list/ >>> 订单相关接口
但是这种接口形式并不能清晰的表明具体作用
并且存在一些特殊接口,并没有一个明确的资源,比如login
'特殊的接口可以出现动词'
http://api/baidu.com/login/
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请求:修改主键为1的书
https://api.baidu.com/books/1 - delete请求:删除主键为1的书
'主要是运用到以上几种请求方式,也可能是以上几种请求的变种,大体就是这样'
6.请求地址中携带过滤条件
https://api.baidu.com/books?name=小&price<20
'相当于我们通过get请求后面的?携带我们需要的过滤条件'
7.响应状态码:两套
1.http响应状态码:
1xx 响应中--临时状态码,表示请求已经接受,告诉客户端应该继续请求或者如果它已经完成则忽略它
2xx 成功--重定向到其它地方;它让客户端再发起一个请求以完成整个处理
3xx 重定向--重定向到其它地方;它让客户端发起一个请求以完成整个处理
4xx 客户端错误--处理发生错误,责任在客户端,如:客户端的请求一个不存在的资源,客户端未被授权,禁止访问等
5xx 服务端错误--处理发生错误,责任在服务端,如:服务端抛出异常,路由出错,HTTP版本不支持等
2.公司规定的响应状态码,放在响应体中
{"code":10000}
8.返回数据中携带错误信息
{"code":10000,"msg":"ok/error"}
9.返回结果应该符合以下规范
GET '获取所有数据':返回资源对象的列表(数组)
相当于[{"name":"xiaoming","age":18},{"name":"dabai","age":12},{"name":"xiaohei","age":38}]
GET '单个对象': 返回单个资源对象 >>> {"name":"dabai","age":12}
POST '新增对象': 返回新生成的资源对象 >>> {"name":"dabai","age":12}
PUT '修改对象': 返回修改后的资源对象 >>> {"name":"dabai","age":13}
DELETE '删除对象' 返回空文档
10.响应数据中带连接
返回结果中提供链接,连向其他api方法。
# 示例:
{
"id": 1404376560,
"description": "人生五十年,乃如梦如幻;有生斯有死,壮士复何憾。",
"url": "http://blog.sina.com.cn/zaku",
"profile_image_url": "http://tp1.sinaimg.cn/1404376560/50/0/1",
"domain": "zaku",
}
序列化反序列化
# api接口开发,最核心最常见的一个过程就是序列化,所谓序列化就是把【数据转换格式】,序列化可以分两个阶段:
# 序列化: 把我们识别的数据转换成指定的格式提供给别人
-例如:我们在django中获取到的数据默认是模型对象,但是模型对象数据无法直接提供给前端或别的平台使用,
所以我们需要把数据进行序列化,变成字符串或者json数据,提供给别人
# 反序列化:把别人提供的数据转换/还原成我们需要的格式。
-例如:前端js提供过来的json数据,对于python而言就是字符串,
我们需要进行反序列化换成模型类对象,这样我们才能把数据保存到数据库中。
基于django编写5个接口
# 查询全部
views视图层代码:
class BookView(View):
def get(self, request):
book_queryset = models.Book.objects.all() # 查询出来为queryset对象
book_list = []
for book_obj in book_queryset:
book_list.append({"name": book_obj.name, "price": book_obj.price, "publish": book_obj.publish})
return JsonResponse(book_list, safe=False)
urls路由层代码
from django.contrib import admin
from django.urls import path
from app01 import views
urlpatterns = [
path('admin/', admin.site.urls),
path('book/',views.BookView.as_view()),
# 新增一条数据
views视图层代码:
class BookView(View):
def get(self, request):
book_queryset = models.Book.objects.all() # 查询出来为queryset对象
book_list = []
for book_obj in book_queryset:
book_list.append({"name": book_obj.name, "price": book_obj.price, "publish": book_obj.publish})
return JsonResponse(book_list, safe=False)
def post(self, request):
name = request.POST.get('name')
price = request.POST.get('price')
publish = request.POST.get('publish')
book_obj = models.Book.objects.create(name=name, price=price, publish=publish)
return JsonResponse({"name": book_obj.name, "price": book_obj.price, "publish": book_obj.publish})
# 查询一条数据
因为是查询数据,我们需要新增加一个路由,因为我们需要在路径后面添加过滤条件
views视图层代码
class BookDetailView(View):
def get(self, request, pk): # 因为使用了转换器所以需要增加一个我们自定义的pk参数
book_obj = models.Book.objects.filter(pk=pk).first()
return JsonResponse({"name": book_obj.name, "price": book_obj.price, "publish": book_obj.publish}, safe=False)
urls路由层代码
from django.contrib import admin
from django.urls import path
from app01 import views
urlpatterns = [
path('admin/', admin.site.urls),
path('book/',views.BookView.as_view()),
path('book/<int:pk>',views.BookDetailView.as_view()) # 使用转换器接收参数
]
# 修改一条数据
views视图层代码
class BookDetailView(View):
def put(self, request, pk):
# 在put 提交的数据,不能从request.POST中取出
dic = json.loads(request.body)
print(dic)
name = dic.get('name')
price = dic.get('price')
publish = dic.get('publish')
book_obj = models.Book.objects.filter(pk=pk).first()
book_obj.name = name
book_obj.price = price
book_obj.publish = publish
book_obj.save()
return JsonResponse({"name": book_obj.name, "price": book_obj.price, "publish": book_obj.publish},safe=False)
urls路由层代码
path('book/<int:pk>',views.BookDetailView.as_view()) # 使用转换器接收参数
修改数据我们使用PUT请求,但是注意我们在提交修改数据的时候因为put提交数据不能从request.POST中获取,
我们只能在 request.body中取出,我们在携带数据的时候就需要使用原生的raw>>>携带json格式数据到后端进行处理
# 删除数据
views视图层代码
def delete(self,request,pk):
models.Book.objects.filter(pk=pk).delete()
return JsonResponse(data={}) # jsonresponse 需要返回一个 空数据 !
urls路由层代码
path('book/<int:pk>',views.BookDetailView.as_view()) # 使用转换器接收参数
drf介绍和快速使用
djangorestframework: drf可以帮助我们快速的实现符合restful规范的接口
django 最新到4.x版本 一般我们会使用3.x版本的django
drf 最新支持到django3.x 但是不支持2.x版本
我们现在使用的是django的2.2.22版本
# 安装 drf
1.我们先安装 djangoframework
pip3.8 install djangorestframework -i http://pypi.douban.com/simple/ --trusted-host pypi.douban.com/simple/
# 由于你是django2.x 它发现它不支持,它会自动卸载dajgno,安装最新的django 4.x
# 但是我们在2.2.22版本还有没有编写完的项目,所以我们需要在安装一下2.2.22版本的django即可
pip3.8 install django==2.2.22 -i http://pypi.douban.com/simple/ --trusted-host pypi.douban.com/simple/
快速使用 drf编写接口
我们在django原生项目框架中编写了5个api接口 , 比较繁琐
我们使用drf 即可快速的帮我们实现接口编写!并且符合restful规范
# views中
from .serializer import BookSerializer
from rest_framework.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(serializers.ModelSerializer):
class Meta:
model = Book
fields = '__all__'
# urls中
from rest_framework.routers import SimpleRouter
router = SimpleRouter()
router.register('books', views.BookView, 'books')
urlpatterns = [
path('admin/', admin.site.urls),
]
# 两个列表相加 [1,2,4] + [6,7,8]=
urlpatterns += router.urls
CBV 流程推导
在django项目时候已经分析过cbv的流程了
再次分析以下 >>>目的 为什么 cbv 可以 自动触发 不同请求方式的视图函数
首先是 urls 路由层中
path('book/',views.BookView.as_view()),
Bookview.as_view() 执行顺序最高
我们可以看以下 as_view() 源码
@classonlymethod
def as_view(cls, **initkwargs):
我们可以看到 它是绑定给类的方法
也就是 我们BookView这个类的方法
@classonlymethod
def as_view(cls, **initkwargs):
"""Main entry point for a request-response process."""
for key in initkwargs:
if key in cls.http_method_names:
raise TypeError("You tried to pass in the %s method name as a "
"keyword argument to %s(). Don't do that."
% (key, cls.__name__))
if not hasattr(cls, key):
raise TypeError("%s() received an invalid keyword %r. as_view "
"only accepts arguments that are already "
"attributes of the class." % (cls.__name__, key))
def view(request, *args, **kwargs):
self = cls(**initkwargs)
if hasattr(self, 'get') and not hasattr(self, 'head'):
self.head = self.get
self.setup(request, *args, **kwargs)
if not hasattr(self, 'request'):
raise AttributeError(
"%s instance has no 'request' attribute. Did you override "
"setup() and forget to call super()?" % cls.__name__
)
return self.dispatch(request, *args, **kwargs)
view.view_class = cls
view.view_initkwargs = initkwargs
# take name and docstring from class
update_wrapper(view, cls, updated=())
# and possible attributes set by decorators
# like csrf_exempt from dispatch
update_wrapper(view, cls.dispatch, assigned=())
return view
这个函数返回了一个 view ,这个view函数 是在as_view函数内部的!
到这个函数内部它有一行代码
self = cls(**initkwargs)
就相当于是 BookView 生成的 对象,
return self.dispatch(request, *args, **kwargs)
返回了self.dispatch 将 request,和如果携带转换器的参数传入
def dispatch(self, 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
return handler(request, *args, **kwargs)
handler 通过反射 getattr 拿到 request.method 中的请求方式
并且 return 请求方式(request, *args, **kwargs)
就相当于自动触发了 BookView中的 视图函数