DRF从入门到入土(二)
一、APIView基本使用
使用view+JsonResponse获取所有图书接口
安装drf: pip3 install djangorestframework
settings.py注册app:
'rest_framework' # 注册drf的app,如果不注册,使用浏览器访问会报错,注册成功则会返回一个渲染过得页面,不仅仅是JSON格式数据
models.py
from django.db import models
# Create your models here.
class Book(models.Model):
name = models.CharField(max_length=32, verbose_name='书籍名')
price = models.CharField(max_length=32, verbose_name='书籍价格')
publish = models.CharField(max_length=32, verbose_name='书籍出版社')
views.py
from django.views import View
from .models import Book
创建视图类,继承View获取所有图书的接口
class BookView(View):
def get(self, request):
book_list = Book.objects.all() # book是queryset对象,不能直接序列化,只能通过for循环拼成列表套字典的形式取出
obj_list = []
for book in book_list:
obj_list.append({'name': book.name, 'price': book.price, 'publish': book.publish})
return JsonResponse(obj_list, safe=False, json_dumps_params={'ensure_ascii': False}) # 只能序列化字典和列表
页面展示
使用APIView+drf的Response获取所有图书接口
from django.views import View
from .models import Book
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.request import Request
# 继承APIView,使用视图类View+响应类Response获取所有图书接口
class BookView(APIView): # APIView继承的是django的View
def get(self, request):
book_list = Book.objects.all() # book是queryset对象,不能直接序列化,只能通过for循环拼成列表套字典的形式取出
obj_list = []
for book in book_list:
obj_list.append({'name': book.name, 'price': book.price, 'publish': book.publish})
return Response(obj_list)
Postman工具测试成功
浏览器访问报错
修改setting.py配置文件
重新使用浏览器访问
二、APIView源码分析
# 视图类继承APIView后,执行流程就发生了变化,这个变化就是整个的drf的执行流程
# 一旦继承了APIView入口
-路由配置跟之前继承View是一样的----》找视图类的as_view---》【APIView的as_view】
@classmethod
def as_view(cls, **initkwargs):
# 使用super又调用了父类(View)的as_view
view = super().as_view(**initkwargs)
'''
# 从此以后,所有的请求都没有csrf的校验了
# 在函数上加装饰器
@csrf_exempt
def index(request):
pass
本质等同于 index=csrf_exempt(index)
'''
return csrf_exempt(view)
-请求来了,路由匹配成功会执行 View类的的as_view类方法内的view闭包函数(但是没有了csrf认证),
-真正的执行,执行self.dispatch---->APIView的dispatch 【这是重点】
def dispatch(self, request, *args, **kwargs):
# 参数的request是原来的django原生的request
# 下面的request,变成了drf提供的Request类的对象---》return Request(。。。)
request = self.initialize_request(request, *args, **kwargs)
# self 是视图类的对象,视图类对象.request=request 新的request
self.request = request
try:
# 执行了认证,频率,权限 [不读]
self.initial(request, *args, **kwargs)
# 原来的View的dispatch的东西
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 以后视图类中使用的request对象,已经变成了drf提供的Request类的对象了
3 执行视图类的方法之前,执行了3大认证(认证,权限,频率)
4 在执行三大认证和视图类的方法过程中只要报错,都会被捕获处理
三、Request源码分析
#1 视图类中使用的request对象,已经变成了drf提供的Request类的对象了
-原生djagno 的request是这个类的对象:django.core.handlers.wsgi.WSGIRequest
-drf的request是这个类的对象:rest_framework.request.Request
#2 request已经不是原来的request了,还能像原来的request一样使用吗?
-用起来,像之前一样
print(request.method) # get
print(request.path) # /books/
print(request.GET) # 原来的get请求提交的参数
print(request.POST) # 原来post请求提交的参数
#3 Request的源码分析:rest_framework.request.Request
-类中有个魔法方法:__getattr__ 对象.属性,属性不存在会触发它的执行
def __getattr__(self, attr): # 如果取的属性不存在会去原生django的request对象中取出来
try:
#反射:根据字符串获取属性或方法,self._request 是原来的request
return getattr(self._request, attr)
except AttributeError:
return self.__getattribute__(attr)
-以后用的所有属性或方法,直接用就可以了---》(通过反射去原来的request中取的)
-新的request内部有个老的request,就是 request._request
-data 是个方法,被property装饰了,变成了数据属性用
-以后body体中提交的数据,都从这里取(request.POST)
-urlencoded,form-data:提交的数据在request.POST中
-json格式提交的数据,在requets.POST中没有,它在request.body中
-现在无论那种格式,都从request.data中取
-query_params:get请求提交的参数,等同于request._request.GET 或 request.GET
-其他:取文件也是从request.FILES中取,跟之前一样
# 验证 原生requets.POST 只有urlencoded和form-data格式提交的数据,json格式提交的数据在body中,拿出来自己处理,但是drf的request中有个data,data中可以取到任意编码提交的数据
# request.data 有时候是(urlencoded,form-data)QueryDict,有时候(json)是字典
# 4 什么是魔法方法?
1 在类中只要以__开头,__结尾的都称之为魔法方法
2 这种方法不需要手动调用,某种情况会自动触发
3 你学过的: __init__,__str__,__call__,......
四、序列化组件介绍
# 序列化组件:drf提供的一个类,我们继承它,可以写自己的类
# 序列化组件的作用: 就是用来序列化qs或单个对象的
# 获取所有图书接口----》qs,单个book对象转成json格式字符串,给前端---》序列化
-使用for循环,列表套字典拼接的
# drf提供了一种可以快速实现序列化的类:序列化类
五、序列化组件基本使用
定义一个序列化类
# 写一个序列化类Book
from rest_framework import serializers
class BookSerializer(serializers.Serializer):
# 序列化哪些字段,括号内可以有很多字段属性、字段类
name = serializers.CharField()
price = serializers.CharField()
publish = serializers.CharField()
使用序列化类,序列化多条数据
from rest_framework.views import APIView
from rest_framework.response import Response
from .serializer import BookSerializer
class BookView(APIView):
def get(self, request):
book_list = Book.objects.all()
ser = BookSerializer(instance=book_list, many=True)
# instance表示要序列化的数据,many=True表示序列化多条数据
return Response(ser.data) # 直接序列化
结果验证
使用序列化类,序列化单条数据
路由层urls.py
from django.contrib import admin
from django.urls import path
from app01.views import BookView, BookDetailView
urlpatterns = [
path('admin/', admin.site.urls),
path('books/', BookView.as_view()),
path('books/<int:pk>/', BookDetailView.as_view())
]
视图层views.py
# 获取单条数据的url地址是这种格式的: http://127.0.0.1/books/1 1指代的是要查询的数据的id号
class BookDetailView(APIView):
def get(self, request, pk):
book = Book.objects.filter(pk=pk).first()
ser = BookSerializer(instance=book)
return Response(ser.data)
结果验证
六、反序列化组件基本使用
反序列化之新增数据(需要进行数据校验)
视图类
from rest_framework.views import APIView
from rest_framework.response import Response
from .serializer import BookSerializer
class BookView(APIView):
# 前端传入的数据,从request.data中获取
def post(self, request):
ser = BookSerializer(data=request.data) # 把前端传入的数据给data属性,通过BookSerializer实例化得到ser对象
if ser.is_valid(): # 校验前端传入的数据 虽然没写校验规则,但是流程还在
ser.save() # 调用save会触发BookSerializer的save方法,它进行判断后,如果instance有值执行update,没有值执行create,所有我们在序列化类里面重写了create方法
return Response(ser.data) # 校验成功返回数据
else:
return Response(ser.errors) # 否则返回错误信息
序列化类
from rest_framework import serializers
from .models import Book
class BookSerializer(serializers.Serializer):
# 序列化哪些字段,括号内可以有很多字段属性、字段类
name = serializers.CharField()
price = serializers.CharField()
publish = serializers.CharField()
# 重写create方法(因为反序列化新增数据时,调用save方法会报错)
def create(self, validated_data): # validated_data就是校验过后的数据
add = Book.objects.create(**validated_data)
return add
结果验证
反序列化之修改数据
视图类
from rest_framework.views import APIView
from rest_framework.response import Response
from .serializer import BookSerializer
# 修改单条数据的url地址是这种格式的: http://127.0.0.1/books/1 1指代的是要查询的数据的id号
class BookDetailView(APIView):
def get(self, request, pk):
book = Book.objects.filter(pk=pk).first()
ser = BookSerializer(instance=book)
return Response(ser.data)
def put(self, request, pk):
book = Book.objects.filter(pk=pk).first() # 根据pk取到book对象
ser = BookSerializer(instance=book, data=request.data) # 使用data数据修改book对象
if ser.is_valid(): # 校验前端传入的数据 虽然没写校验规则,但是流程还在
ser.save() # 调用save会触发BookSerializer的save方法,判断了,如果instance有值执行update,没有值执行create
return Response(ser.data) # 校验成功返回数据
else:
return Response(ser.errors) # 否则返回错误信息
序列化类
from rest_framework import serializers
from .models import Book
class BookSerializer(serializers.Serializer):
# 序列化哪些字段,括号内可以有很多字段属性、字段类
name = serializers.CharField()
price = serializers.CharField()
publish = serializers.CharField()
# 重写update方法(反序列化修改数据时,调用save方法会报错)
def update(self, instance, validated_data):
# instance就是我们要修改的对象(instance=book)
# validated_data就是校验过后的数据
instance.name = validated_data.get('name') # 对象点name = 校验过后的数据中的name
instance.price = validated_data.get('price')
instance.publish = validated_data.get('publish')
instance.save() # 调用save方法保存
return instance # 最后把instance返回出去
结果验证
反序列化之删除数据
视图类
class BookDetailView(APIView):
def delete(self, request, pk):
Book.objects.filter(pk=pk).delete()
return Response()
结果验证
七、HTTP各版本区别
HTTP协议
超文本传输协议(Hyper Text Transfer Protocol,HTTP)是一个简单的请求-响应协议,它通常运行在TCP之上。它指定了客户端可能发送给服务器什么样的消息以及得到什么样的响应。HTTP使用的默认端口号是80端口
HTTPS使用的默认端口是443端口
HTTP协议发展
http协议目前有4个版本,其中1.0和1.1版本在互联网上被广泛使用,2.0版本目前应用很少,是下一代的http协议。
http/0.9版本:1991年,原型版本,功能简陋,只有一个命令GET,服务器只能回应HTML格式字符串,该版本已过时。
http/1.0版本:1996年5月,支持cache、MIME、method等。
http/1.1版本:1997年1月,默认建立持久连接,并能很好地配合代理服务器工作。还支持以管道方式在同时发送多个请求,以便降低线路负载,提高传输速度。
http/2 版本:2015年5月作为互联网标准正式发布,头部信息和数据体都是二进制,引入头信息压缩机制等。
HTTP1.0版本
任何格式的内容都可以发送,这使得互联网不仅可以传输文字,还能传输图像、视频、二进制等文件。
除了GET命令,还引入了POST命令和HEAD命令。
http请求和回应的格式改变,除了数据部分,每次通信都必须包括头信息(HTTP header),用来描述一些元数据。
头信息是 ASCII码,而后面数据可为任何格式。服务器回应时会告诉客户端,数据是什么格式,即Content-Type字段的作用。这些数据类型总称为MIME即多用途互联网邮件扩展,每个值包括一级类型和二级类型,预定义的类型,也可自定义类型,常见Content-Type值:text/xml image/jpeg audio/mp3
每个TCP连接只能发送一个请求,发送数据完毕,连接就关闭,如果还要请求其他资源,就必须再新建一个连接
关于HTTP消息头
HTTP消息头是在客户端请求(Request)或服务器响应(Response)时传递的。以明文的字符串格式传送,是以冒号分隔的键/值对,如:Accept-Charset: utf-8,每一个消息头最后以回车符(CR)和换行符(LF)结尾。HTTP消息头结束后,会用一个空白的字段来标识,这样就会出现两个连续的CR-LF。
HTTP1.1版本
# http1.1是目前最为主流的http协议版本。
引入了持久连接( persistent connection),即TCP连接默认不关闭,可以被多个请求复用,默认Connection: keep-alive,如果响应方不支持keep-alive,则修改为Connection: close
引入了管道机制(pipelining),即在同一个TCP连接里,客户端可以同时发送多个请求(大多数浏览器允许同时建立6个持久连接引入了管道机制),进一步改进了HTTP协议的效率。
新增方法:PUT、 PATCH、 OPTIONS、 DELETE。
http协议不带有状态,每次请求都必须附上所有信息。请求的很多字段都是重复的,浪费带宽,影响速度。
# 其实 http1.X 还是有些问题没有解决的:
传输数据是明文,客户端和服务器端都无法验证对方的身份,无法保证数据的安全性。
header头部数据太长。
每次传输还是要重新连接。
server不能主动push。
1.1版本允许复用TCP连接,但是同一个TCP连接里面,所有的数据通信是按次序进行的。服务器只有处理完一个回应,才会进行下一个回应,可能会造成Head-of-line blocking(队头阻塞)的问题。
HTTP2.x版本
http/2发布于2015年,目前应用还比较少,该版本主要有如下特点:
http/2是一个彻底的二进制协议,头信息和数据体都是二进制,并且统称为"帧"(frame):头信息帧和数据帧。
复用TCP连接,在一个连接里,客户端和浏览器都可以同时发送多个请求或回应,且不用按顺序一一对应,避免了队头堵塞的问题。此双向的实时通信称为多工(Multiplexing)。
HTTP/2 允许服务器未经请求,主动向客户端发送资源,即服务器推送。
引入头信息压缩机制(header compression),头信息使用gzip或compress压缩后再发送;客户端和服务器同时维护一张头信息表,所有字段都会存入这个表,生成一个索引号,不发送同样字段,只发送索引号,提高速度。
八、自主练习
1 继承apiview写5个接口
from .models import Book
from rest_framework.views import APIView
from django.http import JsonResponse
class BookView(APIView):
def get(self, request):
books = []
book_list = Book.objects.all()
for book in book_list:
books.append({'name': book.name, 'price': book.price, 'publish': book.publish})
return JsonResponse(books, safe=False, json_dumps_params={'ensure_ascii': False})
def post(self, request):
name = request.data.get('name')
price = request.data.get('price')
publish = request.data.get('publish')
book = Book.objects.create(name=name, price=price, publish=publish)
return JsonResponse({'info': '新增成功'})
class BookDetailView(APIView):
def get(self, request, pk):
book = Book.objects.all().filter(pk=pk).first()
return JsonResponse({'id': book.id, 'name': book.name, 'price': book.price, 'publish': book.publish})
def delete(self, request, pk):
Book.objects.all().filter(pk=pk).delete()
return JsonResponse({'info': '删除成功'})
def put(self, request, pk):
book = Book.objects.all().filter(pk=pk).first()
book.name = request.data.get('name')
book.price = request.data.get('price')
book.publish = request.data.get('publish')
book.save()
return JsonResponse({'info': '修改成功'})
标签:26,self,09,request,book,2022,import,序列化,data
From: https://www.cnblogs.com/dy12138/p/16732594.html