drf之Restful规范序列化和反序列,drf介绍和快速使用和APIView源码分析
一、Restful规范
RESTful是一种定义API接口的设计风格,AIP接口的编写规范,,尤其适用于前后端分离的应用模式中
这种风格的理念认为后端开发任务就是提供数据的,对外提供的是数据资源的访问接口,所以在定义接口时,客户端访问的URL路径就表示这种要操作的数据资源
我们可以使用任何一个框架都可以实现符合restful规范的API接口
10条规范
1 数据的安全保障,通常使用https协议进行传输
2 url地址中带接口标识:一般这样
-https://api.baidu.com
-https://www.baidu.com/api
3 多版本共存,url地址中带版本信息
https://api.baidu.com/v1/login/
https://api.baidu.com/v2/login/
4 数据即是资源,均使用名词:
url地址尽量使用名词
# 接口一般都是完成前后台数据的交互,交互的数据我们称之为资源
https://api.baidu.com/users
https://api.baidu.com/books
https://api.baidu.com/book
注:一般提倡用资源的复数形式,在url链接中不要出现操作资源的动词,错误示范:https://api.baidu.com/delete-user
# 特殊的接口可以出现动词,因为这些接口一般没有一个明确的资源,或是动词就是接口的核心含义
https://api.baidu.com/place/search
https://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 url地址中带过滤条件 ?后带过滤条件
https://api.baidu.com/books -get请求表示查询所有图书,要查名字中有红的图书
https://api.baidu.com/books?name_contains=红
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的响应状态码:https://blog.csdn.net/meng2lin/article/details/128955775
-1xx:请求正在处理
-2xx:请求成功 200 201
-3xx:重定向
-4xx:客户端错误
-5xx:服务的错误
-http的响应的数据中带状态码(公司自己规定的)
-{code:100}
8 返回的数据中带错误信息
{code:101,msg:用户名或密码错误}
{code:100,msg:成功}
9 返回结果,针对不同操作,服务器向用户返回的结果应该符合以下规范
GET /books:返回资源对象的列表(数组)
-[{name:金瓶,price:88},{name:西游记,price:88}]
-{code:100,msg:成功,data:[{name:金瓶,price:88},{name:西游记,price:88}]}
GET /books/1:返回单个资源对象
-{name:金瓶,price:88} ---{code:100,msg:成功,data:{name:金瓶,price:88}}
POST /books:返回新生成的资源对象
-{id:4,name:金瓶,price:88} ---{code:100,msg:成功}
PUT /books/4:返回完整的资源对象
-{id:4,name:金瓶,price:188} ---{code:100,msg:修改成功}
DELETE /books/4: 返回一个空文档 ---{code:100,msg:删除成功}
10 返回的结果中带url链接
二、序列化和反序列
api接口开发,最核心最常见的一个过程就是序列化
序列化:
把我们识别的数据转换成指定的格式提供给别人。
例如:我们在django中获取到的数据默认是模型对象(queryset),但是模型对象数据无法直接提供给前端或别的平台使用,所以我们需要把数据进行序列化,变成字符串或者json数据,提供给别人。
反序列化:
把别人提供的数据转换/还原成我们需要的格式。
例如:前端js提供过来的json数据,对于python而言就是字符串,我们需要进行反序列化换成模型类对象,这样我们才能把数据保存到数据库中
ps:
序列化:drf称为 read 序列化
反序列化:drf称为 write 反序列化
三、作业:基于django原生编写5个接口
book表为例,写5个接口(后面你写的所有接口,都是这5个,及其变形)
-查询所有图书
-新增一本图书
-修改一本图书
-查询一本图书
-删除一本图书
models.py
from django.db import models
# Create your models here.
class Book(models.Model):
name = models.CharField(max_length=32)
price = models.IntegerField()
publish = models.CharField(max_length=32)
urls.py
from app02 import views
urlpatterns = [
path('admin/', admin.site.urls),
path('books/api/', views.BookView.as_view()),
path('books/api/<int:pk>/', views.BookDetailView.as_view()),
]
views.py
from django.shortcuts import render,HttpResponse
# Create your views here.
from django.http import JsonResponse
from .models import Book
from django.views import View
import json
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,前端就显示中文了
return JsonResponse(book_list, safe=False, json_dumps_params={'ensure_ascii': False}) # JsonResponse只能放字典或列表
# 新增一个(只能使用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})
class BookDetailView(View):
# 获取单条
def get(self, request, pk):
book = Book.objects.filter(pk=pk).first()
# book 模型对象转成字典,使用JsonResponse返回
return JsonResponse({'id': book.id, 'name': book.name, 'price': book.price, 'publish': book.publish})
def put(self, request, pk): # request.POST只能取post提交的urlencoded或form-data编码数据,put提交的取不到
# 查到要改的
book = Book.objects.filter(pk=pk).first()
# 取出前端传入的数据,修改完,保存-----》存在问题,因为put提交的取不到
# book.name = request.POST.get('name')
# book.price = request.POST.get('price')
# book.publish = request.POST.get('publish')
# book.save()
# 前端使用json格式提交,自己保存
print(request.body)
book_dic = json.loads(request.body)
book.name = book_dic.get('name')
book.price = book_dic.get('price')
book.publish = book_dic.get('publish')
book.save()
return JsonResponse({'id': book.id, 'name': book.name, 'price': book.price, 'publish': book.publish})
def delete(self, request, pk):
Book.objects.filter(pk=pk).delete()
return JsonResponse(data={})
3 drf介绍和快速使用
3.1 drf介绍
django 中有个app,djangorestframework:drf,帮助我们,快速实现符合resful规范的接口
下载:(有个坑)
pip3.8 install djangorestframework==稍微将版本
如果你是django2, 直接这样装,装最新drf,他们不匹配---》pip会自动把django卸载,安装最新django,安装最新drf
django3 ,这样没有任何问题
pip3.8 install djangorestframework --upgrade
ps:补充一点点东西:
-如果写了一个包,或app,想给别人用---》把你写的包,放到pypi上别人pip install 安装---》使用
3.2 drf快速使用(不需要会)
urls.py
from app01.views import BookView
from rest_framework.routers import SimpleRouter
router = SimpleRouter()
router.register('books', BookView, 'books')
urlpatterns = [
]
urlpatterns += router.urls
views.py
from .serializer import BookSerializer
from rest_framework.viewsets import ModelViewSet
class BookView(ModelViewSet):
queryset = Book.objects.all()
serializer_class = BookSerializer
序列化类 serializer.py
from rest_framework import serializers
from .models import Book
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = Book
fields = '__all__'
五、 drf之APIView源码分析
4.1 基于APIView的5个接口
视图类
from rest_framework.views import APIView # APIView继承了djagno原来的View
from .serializer import BookSerializer
from rest_framework.response import Response
class BookView(APIView):
# 查询所有
def get(self, request):
book_list = Book.objects.all()
# drf提供了序列化类(先别关注)
ser = BookSerializer(instance=book_list, many=True) # 序列化
return Response({'code': 100, 'msg': '成功', 'result': ser.data})
def post(self, request):
ser = BookSerializer(data=request.data) # 反序列化
if ser.is_valid(): # 数据校验---》有些不合法的禁止
ser.save() # 保存到数据库中
return Response({'code': 100, 'msg': '成功'})
class BookDetailView(APIView):
# 查询单条
def get(self, request, pk):
book = Book.objects.filter(pk=pk).first()
ser = BookSerializer(instance=book, many=False) # 序列化
return Response({'code': 100, 'msg': '成功', 'result': ser.data})
# 修改一条
def put(self, request, pk):
book = Book.objects.filter(pk=pk).first()
ser = BookSerializer(instance=book, data=request.data) # 反序列化
if ser.is_valid(): # 数据校验---》有些不合法的禁止
ser.save() # 保存到数据库中
return Response({'code': 100, 'msg': '成功'})
def delete(self, request, pk):
Book.objects.filter(pk=pk).delete()
return Response({'code': 100, 'msg': '删除成功'})
from rest_framework.views import APIView # APIView继承了djagno原来的View
from .serializer import BookSerializer
from rest_framework.response import Response
class BookView(APIView):
# 查询所有
def get(self, request):
book_list = Book.objects.all()
# drf提供了序列化类(先别关注)
ser = BookSerializer(instance=book_list, many=True) # 序列化
return Response({'code': 100, 'msg': '成功', 'result': ser.data})
def post(self, request):
ser = BookSerializer(data=request.data) # 反序列化
if ser.is_valid(): # 数据校验---》有些不合法的禁止
ser.save() # 保存到数据库中
return Response({'code': 100, 'msg': '成功'})
class BookDetailView(APIView):
# 查询单条
def get(self, request, pk):
book = Book.objects.filter(pk=pk).first()
ser = BookSerializer(instance=book, many=False) # 序列化
return Response({'code': 100, 'msg': '成功', 'result': ser.data})
# 修改一条
def put(self, request, pk):
book = Book.objects.filter(pk=pk).first()
ser = BookSerializer(instance=book, data=request.data) # 反序列化
if ser.is_valid(): # 数据校验---》有些不合法的禁止
ser.save() # 保存到数据库中
return Response({'code': 100, 'msg': '成功'})
def delete(self, request, pk):
Book.objects.filter(pk=pk).delete()
return Response({'code': 100, 'msg': '删除成功'})
序列化类
from rest_framework import serializers
from .models import Book
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = Book
fields = '__all__'
路由
urlpatterns = [
path('books/', BookView.as_view()),
path('books/<int:pk>/', BookDetailView.as_view()),
]
4.2 CBV源码分析
# cbv写法:
1 视图中写视图类,继承View,写跟请求方式同名的方法
class BookView(View):
def get(self,request):
return 四件套
2 在路径用写
path('books/', BookView.as_view())
# 如上写法,为什么能够执行
# 前置条件:前端请求,一旦路径匹配成功,就会执行 BookView.as_view()(request传入,)
# 入口在 BookView.as_view()--->执行结果---》View中有个as_view类的绑定方法
@classmethod
def as_view(cls, **initkwargs):
def view(request, *args, **kwargs):
self = cls(**initkwargs)
res=self.dispatch(request, *args, **kwargs)
return res
return view
# 执行结果是view 的内存地址: 请求来了,执行view(request)
path('books/', view)
# 执行 View类中的as_view方法中的内层的view函数,路由匹配成功,本质是在执行
self.dispatch(request, *args, **kwargs)
# self是谁的对象?BookView的对象
# 去BookView中dispatch,找不到,去父类,View中找到了
# View这个类的dispatch
def dispatch(self, request, *args, **kwargs):
# request.method.lower() 如果是get请求, ‘get’ 在这个列表里面
if request.method.lower() in self.http_method_names:
# handler=getattr(BookView的对象,'get')
# handler就是BookView类中的get方法
handler = getattr(self, request.method.lower())
else:
handler = self.http_method_not_allowed
# 执行 BookView类中的get方法 (request)
return handler(request, *args, **kwargs)
# 最终本质跟写fbv的执行流程一样
# 最终结论:什么请求方式,就会执行视图类中的什么方法
4.3 APIView执行流程分析
# 有了drf,后期都写CBV,都是继承APIView及其子类
# 执行流程:
-入口:path('books/', BookView.as_view())---》请求来了,执行BookView.as_view()(request)
-as_view 是谁的? APIView的as_view
@classmethod
def as_view(cls, **initkwargs):
# super()代指的是:父类对象 View类的对象
# View的as_view(**initkwargs)----》执行结果是view,是View类的as_view方法中的view
view = super().as_view(**initkwargs)
view=csrf_exempt(view) # 局部禁用csrf,
return view
-path('books/', View类的as_view中的view,只是去掉了csrf的认证)
-请求来了,执行 【View类的as_view中的view,只是去掉了csrf的认证(request)】
-执行:self.dispatch(request, *args, **kwargs), self要从根上找
-self.dispatch 是APIView的dispatch,源码如下
def dispatch(self, request, *args, **kwargs):
# request 是新的request, request是老的request
request = self.initialize_request(request, *args, **kwargs)
self.request = request
try:
# 执行了认证,权限和频率
self.initial(request, *args, **kwargs)
# 在执行视图类方法之前,去掉了csrf认证,包装了新的request,执行了认证频率和权限
#### 执行请求方式字符串对应的方法
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
# 总结:
1 以后只要继承APIView的所有视图类的方法,都没有csrf的校验了
2 以后只要继承APIView的所有视图类的方法 中的request是新的request了
3 在执行视图类的方法之前,执行了三大认证(认证,权限,频率)
4 期间除了各种错误,都会被异常捕获,统一处理
# 补充(装饰器语法糖):
fbv,局部禁用csrf,如何写?
@csrf_exempt
def index(request):
pass
本质原理是(装饰器本质):index=csrf_exempt(index)
作业
# 1 写完booke的剩余3个接口
# 2 按照我写的,照着敲(基于APIView的5个接口)
# 3 走一遍cbv和APIView的源码
# 高级:可以不写
-写一个装饰器,装饰在fbv上,这个fbv可以接受前端的编码格式可以是urlencoded,form-data,json,取数据,都是从request.data中取
######### 1 作业讲解############
def load_json(func):
def inner(request, *args, **kwargs):
try:
request.data = json.loads(request.body) # 如果是json格式,就成功;如果是其他格式,直接就报错了
except Exception as e:
request.data = request.POST
res = func(request, *args, **kwargs)
return res
return inner
@load_json
def books(request):
# post提交数据,urlencoded,form-data,json
print(request.data)
print(request.FILES)
return JsonResponse({'code': 100, 'msg': '成功'})