APIView执行流程、Request对象源码分析、序列化器介绍和快速使用、反序列化、反序列化的校验、练习
一、APIView执行流程
1.1基于APIView+JsonResponse编写接口
# def提供了一个APIView类,方便我们使用drf写视图类,我们使用drf写视图类,都是继承这个类及其子类,APIView自身是继承了Django原生的view类
1.1.1 models部分
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='出版社')
1.1.2 urls部分
from django.contrib import admin
from django.urls import path
from books import views
urlpatterns = [
path('admin/', admin.site.urls),
path('books/', views.Books.as_view()),
]
1.1.3views部分
from django.http import JsonResponse
from django.shortcuts import render
from rest_framework.views import APIView
from .models import Book
# Create your views here.
class Books(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)
1.2 基于APIView+Response写接口
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)
# 使用该方法时,遇到问题,Response在浏览器上使用,报错,postman没问题,原因是因为rest_framework是个应用,需要注册下app
1.3 APIView的执行流程
# urls中的path('books/', views.BookView.as_view()),请求来了之后,执行views.BookView.as_view()()--->现在as_view是APIView的as_view
# APIView的as_view方法:
# view还是view,但是以后没有csrf认证了
@classmethod
def as_view(cls, **initkwargs):
# 调用父类的as_view,父类是django原生的View
# 把djagno原生View的as_view方法中的闭包函数view拿出来了
view = super().as_view(**initkwargs)
# csrf_exempt 排除所有csrf的认证
# 相当于在所有的方法上面加了这个装饰器
return csrf_exempt(view)
# 路由匹配成功,执行 csrf_exempt(view)(requets)--->View的as_view中的闭包函数view---》self.dispatch---->self是视图类的对象---》BookiView---》APIView的dispatch,找到了
def dispatch(self, request, *args, **kwargs):
# request是django原生的request,老的request
# 把老的request包装成了新的request,这个是drf提供的Request类的对象
request = self.initialize_request(request, *args, **kwargs)
# 到此以后,这个request就是新的了,老的request在哪?
# request._request 这是老的
# 把新的request放到了self对象【BookView的对象】
self.request = request
try:
# 执行了三大认证【认证,频率,权限】,使用新的request,不读
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
# 把新的request传入了,视图类的方法中get的request也是新的
response = handler(request, *args, **kwargs)
except Exception as exc:
# 在执行3大认证和视图类中方法的过程中,如果出了异常,都能捕获到---》全局异常捕获
response = self.handle_exception(exc)
self.response = self.finalize_response(request, response, *args, **kwargs)
return self.response
1.3.1 APIView的执行流程总结
1、去除了所有的csrf认证
2、包装了新的request,以后在视图类中用的request是新产生的request,Request类的对象,不再是原生的,原生的是:request._request
3、在执行视图类的方法之前,执行了三大认证(认证、校验、频率)
4、如果在三大认证或者视图函数方法执行过程中出现了错误,会有全局捕获--->全局异常捕获
5、以后视图类方法中的request就都是新的request
1.3.2 补充:装饰器的基本原理
def auth() # 装饰器
def add() # 函数
# 使用auth装饰add函数
@auth # 本质是 add=auth(add)
def add()
# 以后再使用add,其实就是在使用 auth(add) 的返回结果
二、 Request对象源码分析
2.1 新的Request和老的Request的区别:
新的Request和老的Request的区别:
老的Request来自:django.core.handlers.wsgi.WSGIRequest
老的是:request._request
新的Request来自:from rest_framework.request import Request
# Request源码
方法:__geyattr__
在视图类的方法中,执行request.method,新的request是没有method的,会触发新的Request的__getattr__方法执行
def __getattr__(self, attr):
try:
# 从老的request中反射出 要取得属性
return getattr(self._request, attr)
except AttributeError:
return self.__getattribute__(attr)
2.2 Request类总结
request.data--->这个方法,封装了数据属性,有了它之后,无论是post、put或是其他的放在请求体body中提交的数据,都可以从request.data中获取,获取到的数据就是字典,无论哪种编码格式
request.query_params--->这个方法,封装了数据的属性,get请求携带的参数可以从这里获取
query_params:查询参数--->restful规范请求地址中带有查询参数
request.FILES--->这个方法,封装了数据属性,前端提交过来的文件,可以从这里获取
总结:
1、新的request用起来,和老的一样,因为新的取不到,或获取到__getattr__
2、request.data 无论什么编码,什么请求方式,只要是body中的数据,就从这里取,获取到的就是字典
3、request.query_params 就是之前的request._request.GET
4、上传的文件可以从request.FILES中获取
2.3 python中的魔法方法
__str__ :打印对象会调用
__init__: 类()会调用
__call__: 对象()会调用
__new__: 在内存中为对象分配空间 返回对象的引用
__getattr__: 对象.属性,如果属性不存在,会触发它的执行
三、序列化器介绍和快速使用
3.1 序列化器简介
# 我们在写接口的时候, 需要序列化、反序列化,而且反序列化的过程中需要做数据的校验--->drf封装了固定的写法,只要按照固定的写法使用,就可以完成响应的需求
# 提供了 Serializer和ModelSerializer
# 我们只需要写自己的类,继承drf的序列化类,使用其中的某些方法,即可完成需求
# 使用APIView+序列化类+Response 完成接口的编写
3.2 序列化类的基本使用,序列化单条
3.2.1 serializer.py--BookSerializer类(序列化类)
# coding:utf-8
from rest_framework import serializers
class BookSerializer(serializers.Serializer):
# 序列化某些字段,这里写需要序列化的字典
# serializer下大致跟models下的类是对应的
name = serializers.CharField()
price = serializers.CharField()
publish = serializers.CharField()
3.2.2 views.py-->BookView类 (视图类)
from .serializer import BookSerializer
class BookView(APIView):
def get(self, request):
# 获取book数据
books =Book.objects.all()
# 使用序列化类来完成---> 需要有个序列化类
# instance要序列化的数据books获取的queryset对象
# many = Ture 只要是queryset对象需要传many = True
# 单个对象不需要传
ser = BookSerializer(instance=books, many=True)
# Response()无论是列表还是字典都可以直接序列化
return Response(ser.data)
3.3 序列化单条
3.3.1 serializer.py--BookSerializer类(序列化类)
# 和多条一样,没有改变
# coding:utf-8
from rest_framework import serializers
class BookSerializer(serializers.Serializer):
# 序列化某些字段,这里写需要序列化的字典
# serializer下大致跟models下的类是对应的
name = serializers.CharField()
price = serializers.CharField()
publish = serializers.CharField()
3.3.2 views.py-->BookDetailView类 (视图类)
class BookDetailView(APIView):
# def get(self, request, pk):
# 也可以使用可变长形参
def get(self, request, *args, **kwargs):
book = Book.objects.filter(pk=kwargs.get('pk')).first()
# 序列化
ser = BookSerializer(instance=book,)
return Response(ser.data)
3.3.3 url (路由)
urlpatterns = [
path('books/<int:pk>', views.BookDetailView.as_view()),
]
四、反序列化
4.1 反序列化的新增
4.1.1 serializer.py--BookSerializer类(序列化类)
# coding:utf-8
from rest_framework import serializers
from books.models import Book
class BookSerializer(serializers.Serializer):
# 序列化某些字段,这里写需要序列化的字典
# serializer下大致跟models下的类是对应的
name = serializers.CharField()
price = serializers.CharField()
publish = serializers.CharField()
# 编写create方法,保存的逻辑
def create(self, validated_data):
# validated_data 校验过后的数据{name, price, publish}
# 保存到数据库中
book = Book.objects.create(**validated_data)
return book
4.1.2 views.py-->BookView类 (视图类)
class BookView(APIView):
def post(self, request):
# request.data 存储着前端提交的要保存的数据--->校验数据
# 把前端传入的要保存的数据给data参数
ser = BookSerializer(data=request.data)
# 校验数据
if ser.is_valid():
# 保存数据,需要在序列化类BookSerializer中写
# 调用ser.save()方法,直接触发我们编写的create方法,保存起来
ser.save()
return Response({'code': 100,
'msg': '新增成功',
'result': ser.data
})
else:
return Response({'code': 101,
'msg': ser.errors,
})
4.2 反序列化的修改
4.2.1 serializer.py--BookSerializer类(序列化类)
class BookSerializer(serializers.Serializer):
# 序列化某些字段,这里写需要序列化的字典
# serializer下大致跟models下的类是对应的
name = serializers.CharField()
price = serializers.CharField()
publish = serializers.CharField()
def update(self, instance, validated_data):
# instance 需要修改的对象,validated_data校验过后前端传来的数据
instance.name = validated_data.get('name')
instance.price = validated_data.get('price')
instance.publish = validated_data.get('publish')
# orm的单个对象,修改了单个对象的属性,只要调用对象.save,就能把修改保存到数据库
instance.save()
return instance
4.2.2 views.py-->BookDetailView类 (视图类)
class BookDetailView(APIView):
def put(self, request, pk):
book = Book.objects.filter(pk=pk).first()
# 反序列化保存 ---借助于序列化类
ser = BookSerializer(data=request.data, instance=book)
if ser.is_valid():
ser.save()
return Response({'code': 100,
'msg': '修改成功',
'result': ser.data
})
else:
return Response({'code': 101,
'msg': ser.errors
})
4.3 删除单条
# views.py-->BookDetailView类 (视图类)
class BookDetailView(APIView):
def delete(self, request, pk):
Book.objects.filter(pk=pk).delete()
return Response({'code': 100,
'msg': '删除成功'
})
五、反序列化的校验
# 序列化类反序列化,数据校验功能--->类比forms组件
# 局部钩子
def validate_name(self, name):
# 校验name是否合法
import re
# 第一个是表达式,第二个是需要校验数据
res = re.findall('^[0-9].*$', name)
if res:
# 校验name是否合法
# 校验不通过,抛异常
raise ValidationError('不能以数字开头')
else:
return name
# 全局钩子
def validate(self, attrs):
# 校验之后的数据,书名和出版社不能一致
if attrs.get('name') == attrs.get('publish'):
raise ValidationError('书名跟出版社名字不能一致')
else:
return attrs
六、练习
原生的request 没有data 属性 实现一个原生的 request.data 拿出无论什么编码格式提交的数
6.1 models
from django.db import models
# Create your models here.
class Publish(models.Model):
publish_name = models.CharField(max_length=32, verbose_name='出版社')
6.2 views
from django.shortcuts import render, HttpResponse
from django.views import View
from .models import Publish
import json
from urllib.parse import unquote
def outer(func):
def inner(request, *args, **kwargs):
try:
request.data = json.loads(request.body)
except:
request.data = request.POST
if request.method == 'PUT':
res = request.body.decode(encoding='utf-8').split('&')
request.data = {}
for res1 in res:
res2 = res1.split('=')
request.data[res2[0]] = unquote(res2[1])
res = func(request, *args, **kwargs)
return res
return inner
@outer
def publish_view(request):
print(request.data)
return HttpResponse('www.baidu.com')
6.3 views
from django.contrib import admin
from django.urls import path
from viewWorker import views
urlpatterns = [
path('publish/', views.publish_view)
]