文章目录
- 一、Request对象源码分析
- 区分原生request和新生request
- 新的request还能像原来的reqeust一样使用吗
- 源码片段分析
- 总结:
- 二、DRF之序列化组件
- 序列化介绍
- 序列化步骤
- 序列化组件的基本使用
- 反序列化基本使用
- 反序列化的新增
- 反序列化的新增
- 删除单条
- 反序列化的校验
一、Request对象源码分析
在上一篇博客中最后分析APIView时,我们分析出继承了APIView的视图,以后request都是新的request了,是DRF提供的Request的对象
区分原生request和新生request
原生request:django.core.handlers.wsgi.WSGIRequest新生request:from rest_framework.request import Request原生request可以在新生request._request中获取
新的request还能像原来的reqeust一样使用吗
用起来屎一样的,新的request只是在原来的request基础上添加了一些功能,并不影响基础功能的使用,其本质就是类似装饰器print(request.method) # getprint(request.path) # /movies/print(request.GET) # 原来的get请求提交的参数print(request.POST) # 原来post请求提交的参数
源码片段分析
'''源码解析之 __init__'''从上面区分原生request和新的request中可以知道,老的request对象其实就是在新的内部,request._request中'我们先看__init__,它是一个初始化方法,类实例化得到对象时,会执行它,然后会往对象中放数据'def __init__(self, request, parsers=None, authenticators=None,negotiator=None, parser_context=None):'''传入的request是老的,django原生的request放到self._request,self是新的request类的对象这里它把传入过来的原request放到这个self._request中,这里的self已经不是视图类了,因为这个Request类没有继承任何一个类,它就是它自己,所以这个self是Request'''self._request = requestself._data = Emptyself._files = Empty.....
在类内部,以 __开头 __结尾的方法, 在某种情况下会自动调用,他们称之为魔法方法。具体有那些,可以自行搜索,因为所有的类都继承了Object类,所以也可以在Object类中看看,但是不全里面
'''源码解析之__getattr__'''类中有个魔法方法:__getattr__,对象.属性,属性不存在会触发它的执行def __getattr__(self, attr): 如果取的属性不存在会去原生的Django的request对象中取出来try:'通过反射,因为这里是self._request所以去Django的request取,能取到就返回,娶不到就执行except代码,如果还取不到则报错'return getattr(self._request, attr)except AttributeError:return self.__getattribute__(attr)'以后用的所有属性或方法,直接用就可以了(通过反射去原来的request中取)'
以后新的request中多了一个属性data,它会把前端post/put提交的请求体中的数据,都放在request.data中,无论何种编码格式,它都是字典
data是一个方法,被property装饰了,变成了数据属性用-以后body中提交的数据,都从这里取(request.POST)-urlencoded,form-data:提交的数据在request.POST中-json格式提交的数据,在request.POST中是没有的,它在request.body中-现在无论那种格式,都可以直接从request.data中取request.query_params:get请求提交的参数,以后从这里取request.FILES:取文件就还是从这个里面取,和之前一样
总结:
1.新的request跟之前的用法一模一样,如果新的request取不到,它使用__getattr__魔法方法去原生request中取。当然原生的也可以直接在新的request中拿到,request._request2.新的request中多了data属性,request.data客户端提交的请求体中的数据,无论是什么编码都在request.data中3.其他的使用和原生的request一模一样request.query_params就是原来的request._request.GET上传的文件从request.FILES'''1.原生Django提交数据(post),只能处理urlencoded和form-data编码,从request.POST中取2.原生Django提交数据(put),处理不了,需要我们从request.body中取出来进行处理分不同编码格式:urlencoded------》例:name=lqz&age=19---》使用字符串切割的方式.splitjson----->{'xxx':'xx','yyy':'yy'}--->需要自己进行json.loads反序列化3.原生Django不能处理json提交的数据(post/put),需要自己做反序列化json----->{'xxx':'xx','yyy':'yy'}--->需要自己进行json.loads反序列化4.新的request解决了所有问题:request.data'''
二、DRF之序列化组件
序列化类(组件)可以做的事情:1.序列化,QuerySet对象,单个对象做序列化给前端2.反序列化数据校验:前端传入数据后校验数据是否合法3.反序列化数据保存:前端传入数据,存到数据库中
序列化介绍
- 在写接口时,需要序列化和反序列化,而且反序列化的过程中要做数据校验,drf直接提供了固定的写法,只需要按照固定写法,只需要按照固定写法使用,就能完成上面的三个需求。
- 提供了两个类
Serializer
、ModelSerializer
,编写自定义的类,只需要继承drf提供的序列化类,就可以使用其中的某些方法,也能完成上面的三个需求
序列化类的作用:做序列化、反序列化、反序列化校验
序列化步骤
1.写一个py文件,叫serializer.py(命名随意)2.写一个序列化类,继承serializers.Serializer,3.在类中编写需要序列化的字段例:name=serializers.CharField()4.在视图类中使用,导入models文件中的类books,然后实例化得到对象,对查出来的query对象们,对单个对象序列化并传入instance=books参数如果query是复数,一定要串many=True,如果query是单个对象,就无需传入many5.序列化类对象:ser.data---->字典或列表----->通过Response将json格式字符串返回给前端
序列化组件的基本使用
1.创建一个py文件 ----》serializer.pyfrom rest_framework import serializersclass BookSerializer(serializers.ModelSerializer):name = serializers.CharField(max_length=18, min_length=2, required=True)price = serializers.IntegerField(required=True)class Meta:model = Bookfields = '__all__'2.view.py文件中from app01 import modelsfrom rest_framework.views import APIViewfrom rest_framework.response import Responsefrom .serializer import BookSerializerclass BookView(APIView):def get(self, request):book_list = models.Book.objects.all()ser = BookSerializer(instance=book_list, many=True) # 序列化多条需要many=Truereturn Response({'code': 100, 'msg': '查询成功', 'results': ser.data}) # 无论是列表还是字典都可以序列化class BookDetailView(APIView):def get(self, request, pk):book_obj = models.Book.objects.filter(pk=pk).first()ser = BookSerializer(instance=book_obj)if ser.is_valid():return Response({'code': 100, 'msg': '查询一条成功', 'results': ser})else:return Response(ser.errors)3.urls.py文件中urlpatterns = [path('books/', views.BookView.as_view()),path('books/<int:pk>', views.BookDetailView.as_view()),]
反序列化基本使用
反序列化过程:新增、修改
新增:1. 前端传入后端的数据,不论编码格式,都在request.data中,request.data格式是字典前端根据传入的编码格式不一样,从request.data取到的字典形式也是不一样的编码格式 字典urlencoded QueryDictform-data QueryDictjson dict2. 将前端传入的数据request.data进行反序列化,并完成序列化类的反序列化3. 序列化类得到对象并传入参数:data=request.data校验数据保存:ser.save()--->序列化类中重写create方法修改:1. 拿到前端传入的数据,进行反序列化,查出要修改的对象--->序列化类的反序列化2. 序列化类得到对象,传入参数:instance=要修改的对象,data=request.data校验数据 保存:ser.save() --->序列化类中重写update方法
反序列化的新增
序列化类
class BookSerializer(serializers.ModelSerializer):name = serializers.CharField()price = serializers.IntegerField()class Meta:model = Bookfields = '__all__'# 新增一条数据def create(self, validated_data):# 保存的逻辑# validated_data 校验过后的数据 {name,price,publish}# 保存到数据库book = Book.objects.create(**validated_data)# 一定不要忘记返回新增的对象return book
视图类
class BookView(APIView):def get(self, request): # 获取多条数据book_list = models.Book.objects.all()'''instance表示要序列化的数据,many=True表示序列化多条(instance是QuerySet对象)'''ser = BookSerializer(instance=book_list, many=True) # 序列化多条需要many=Truereturn Response({'code': 100, 'msg': '查询成功', 'results': ser.data})def post(self, request):ser = BookSerializer(data=request.data) # 从前端传递数据从request.data中取出来if ser.is_valid(): # is_valid表示校验前端传入的数据,但是我们没有写校验规则# 保存,需要自己写,要在序列化类BookSerializer中重写create方法ser.save() # 调用ser.save,自动触发自定义编辑create方法保存数据'''这个时候发送post请求会发生报错,NotImplementedError: `create()` must be implemented.这个时候点击我们点击save查看源码是调用了Save会触发BaseSerializer的方法判断了 如果instance有值执行update,没有值执行create 看到create没有写 所以我们得重写Create'''return Response({'code': 100, 'msg': '添加成功', 'results': ser.data})else:return Response({'code': 101, 'msg': ser.errors})
反序列化的新增
序列化类
class BookSerializer(serializers.ModelSerializer):name = serializers.CharField()price = serializers.IntegerField()class Meta:model = Bookfields = '__all__'# 修改对象def update(self, instance, validated_data):# instance 要修改的对象# validated_date 校验过后的数据instance.name = validated_data.get('name')instance.price = validated_data.get('price')instance.save() # orm的单个对象,修改了单个对象的属性,只要调用对象.save就可以修改保存到数据库return instance # 记得把修改的对象返回
视图类
class BookDetailView(APIView):def get(self, request, pk): # 获取单条数据book_obj = models.Book.objects.filter(pk=pk).first()ser = BookSerializer(instance=book_obj)return Response({'code': 100, 'msg': '查询一条成功', 'results': ser.data})def put(self, request, pk):book_obj = models.Book.objects.filter(pk=pk).first()ser = BookSerializer(instance=book_obj,data=request.data)if ser.is_valid():ser.save() # 同新增一样,需要重写update方法return Response({'code': 100, 'msg': '修改一条成功', 'results': ser.data})else:return Response({'code': 101, 'msg': ser.errors})
删除单条
class BookDetailView(APIView):def delete(self,request,pk):models.Book.objects.filter(pk=pk).delete()return Response({'code': 100, 'msg': '删除一条成功'})
反序列化的校验
反序列化的数据校验功能类比forms组件
- 局部钩子
- 全局钩子
代码实现
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
from .models import Bookclass BookSerializer(serializers.ModelSerializer):name = serializers.CharField(max_length=18, min_length=2, required=True)price = serializers.IntegerField(required=True)publish = serializers.CharField(min_length=3)class Meta:model = Bookfields = '__all__'# 新增一条数据def create(self, validated_data):# 保存的逻辑# validated_data 校验过后的数据 {name,price,publish}# 保存到数据库book = Book.objects.create(**validated_data)# 一定不要忘记返回新增的对象return book# 修改对象def update(self, instance, validated_data):# instance 要修改的对象# validated_date 校验过后的数据instance.name = validated_data.get('name')instance.price = validated_data.get('price')instance.save() # orm的单个对象,修改了单个对象的属性,只要调用对象.save就可以修改保存到数据库return instance # 记得把修改的对象返回# 局部钩子def validate_price(self,price):if price < 10 or price > 999:raise ValidationError('价格不能高于999或者低于10')return price# 全局钩子def validate(self, attrs):# 校验过后的数据,出版社后三位文字与书名后三位不能一样if attrs.get('publish')[-3] == attrs.get('name')[-3]:raise ValidationError('出版社后三位文字不能与书名后三位一样!')return attrs