DRF - 源码分析和序列化器
一、APIView
基于原生Django的View
类编写视图层中视图类接口。
但是,drf中编写视图类,都是通过继承APIView
及其子类来编写视图类,而APIView
本身就是继承了Django原生的View
1.基于APIView
+JsonResponse
写接口
可以看出继承drf的APIView
和基于原生DJango的JsonResponse
来编写接口,和原生Django相差不大,仍然需要考虑传入的是字典还是列表的问题
# 通过drf提供的APIView类,来编写接口
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 JsonResponse(book_list, safe=False)
2.基于drf的APIView
+ Response
写接口
通过drf的Response来写接口,更加简洁无需考虑传过去的是字典还是列表
# 2 通过drf提供的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)
3.APIView的执行流程
阅读APIView的源码分析
(1)路由中
path('books/', views.BookView.as_view())
请求发送给后端之后,执行BookView.as_view()()
,然后执行的是BookView
中的继承的APIView
类的as_view
方法
(2)APIView
的as_view
方法
view派生了原来的view方法,但是通过csrf_exempt
装饰器不在执行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)
(3)路由匹配成功后
执行了csrf_exempt(view)(requets)
方法,相当于执行了View
的as_view
中的闭包函数view
,返回了self.dispatch
方法,这里的self是指视图类BookView
执行后产生的对象,所以去视图类继承的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
- 总结APIView的执行流程
1.通过装饰器,去除了所有的csrf中间件的认证
2.APIView包装产生了新的request
所以在视图类中用的request是新的request,也就是Request类的对象,不是原生Django的request了。
而原生的request被包装成了:requets._request
3.在执行视图类的方法之前,执行了3大认证:认证,频率,权限
4.如果在3大认证、视图函数方法执行过程中报错,会有全局异常捕获来捕获异常进行处理
5.经过上面的流程,继承APIView类的视图类中的request,就是通过drf包装的新request了
二、Request对象源码分析
1.Request类中
request._request
:原生request
request.data
:POST请求提交的数据(urlencoded、json、formdata)request.user
:不是原生的user了request.query_params
:原生的request.GET
,遵循RESTful规范(6.查询参数,也就是GET请求获取的?后的参数
)- 重写了
__getattr__
、新的request.原来的所有属性和方法
:都可以直接用.
拿到
图片失效了 需要补充
三、序列化器的基本使用
1.序列化
- 序列化:序列化器会把模型
对象转换成字典
,经过Response以后变成json字符串
Book -> 序列化器 -> 字典 -> 通过DRF的Response -> JSON格式字符串 -> 传给前端
- 反序列化:把客户端发送过来的数据,经过request以后变成字典,序列化器可以把
字典转成模型
JSON格式数据 -> 通过DRF的Response -> 字典 -> 序列化器 -> Book
- 数据校验:在反序列化的时候,能进行数据校验
2.序列化器介绍
drf直接提供了固定的序列化写法,也就是序列化器
-
1.在当前app下,写一个序列化器
serializer.py
,继承serializers.Serializer
-
2.在这个序列化器中,编写要序列化的字段
-
3.在视图中,实例化得到一个序列化类的对象,把要序列化的数据传入
ser = BookSerializer(instance=res, many=True)
- 4.得到一个字段,也就是
JSON格式字符串
3.序列化类的基本使用-序列化
(1)序列化多条
- serializer.py--继承
serializers
类
from rest_framework import serializers
class BookSerializer(serializers.Serializer):
# 要序列化的字段
name = serializers.CharField() # serializers跟models.py中的类是对应的
price = serializers.CharField()
publish = serializers.CharField()
- views.py--
BookView
类
class BookView(APIView):
def get(self, request):
books = Book.objects.all()
# 使用序列化类来完成,instance要序列化的数据books queryset对象
# many=True 只要是queryset对象要传many=True,如果是单个对象就不用传
ser = BookSerializer(instance=books, many=True)
return Response(ser.data) # Response方法无论是列表还是字典都可以序列化
(2)序列化单条
-
url.py中
由于需要传入pk值,则需要新开始路由
path('books/<int:pk>/', views.BookDetailView.as_view()),
-
serializer.py没有改变
-
views.py--
BookDetailView
视图类新增
BookDetailView
视图类来序列化单条数据
class BookDetailView(APIView):
def get(self, request, *args, **kwargs):
book = Book.objects.filter(pk=kwargs.get('pk')).first()
# 序列化单个数据不需要传many=True
ser = BookSerializer(instance=book)
return Response(ser.data)
4.序列化类的基本使用-反序列化
(1)反序列化的新增
serializer.py
新增create
方法
class BookSerializer(serializers.Serializer):
# 1 序列化字段
name = serializers.CharField() # serializers下大致跟models下的类是对应的
price = serializers.CharField()
publish = serializers.CharField()
# 2 新增需要create方法来新增数据
def create(self, validated_data):
# 1 通过校验过后的数据,会以保存在validated_data中
# 2 通过orm来保存到数据库
book = Book.objects.create(**validated_data)
# 3 一定要返回新增的模型对象
return book
- views.py--
BookDetailView
视图类
class BookView(APIView):
def post(self, request):
# 1 前端提交的要保存的数据,通过序列化类后校验的数据保存在request.data中
ser = BookSerializer(data=request.data) # 把前端传入的要保存的数据,给data参数
# 2 校验数据
if ser.is_valid():
# 3 保存数据,需要我们手动在在序列化类BookSerializer中写create方法
ser.save() # 4 调用ser.save,自动出发序列化类create方法,保存数据
return Response({'code': 100, 'msg': '新增成功', 'result': ser.data})
else:
return Response({'code': 101, 'msg': ser.errors})
(2)反序列化的修改
serializer.py
新增update
方法
class BookSerializer(serializers.Serializer):
# 1 序列化字段
name = serializers.CharField() # serializers下大致跟models下的类是对应的
price = serializers.CharField()
publish = serializers.CharField()
# 3 修改数据需要编写update方法
def update(self, instance, validated_data):
# instance指代要修改的数据对象
# validated_data是校验过后的数据
# 1 修改数据,通过讲数据对象中的数据,改变成校验过后的修改的新数据
instance.name = validated_data.get('name')
instance.price = validated_data.get('price')
instance.publish = validated_data.get('publish')
# 2 保存数据,通过orm修改了单个对象的属性,也就是通过orm只要调用save方法,就能把修改后的收据保存到数据库
instance.save()
# 3 返回修改后的数据
return instance
- views.py--
BookDetailView
视图类,编写put
方法
class BookDetailView(APIView):
# 反序列化的修改
def put(self, request, pk):
book = Book.objects.filter(pk=pk).first()
# 1 反序列化保存--通过序列化类
ser = BookSerializer(data=request.data, instance=book)
# 2 通过is_valid校验数据
if ser.is_valid():
# 3 通过序列化类的update方法来修改数据
ser.save()
# 4 通过APIView的Response方法返回信息
return Response({'code': 100, 'msg': '修改成功', 'result': ser.data})
else:
return Response({'code': 101, 'msg': ser.errors})
(3)删除单条
- views.py--
BookDetailView
视图类,编写delete
方法
class BookDetailView(APIView):
# 删除数据
def delete(self, requset, pk):
Book.objects.filter(pk=pk).delete()
return Response({'code': 100, 'msg': '删除成功'})
5.反序列化的校验
校验数据的功能,通过钩子函数:全局钩子和局部钩子,来进行校验数据
(1)局部钩子
校验的数据在validate_
后加校验的字段名
- 例如:书名不能以x结尾
# 校验的数据在validate_后加校验的字段名
def validate_name(self, name):
# 1 判断name是否以x结尾
if name.endswith('x'):
# 2 如果校验不通过,则抛异常返回给前端
raise ValidationError('不能以x开头')
else:
# 3 校验成功后返回数据
return name
(2)全局钩子
全局钩子函数名为:validate
- 例如:校验书名跟出版社名字
def validate(self, attrs): # attrs则是带校验的数据
# 1 获取带校验的数据,判断书名和出版社名是否一致
if attrs.get('name') == attrs.get('publish'):
# 2 如果校验不通过,则抛异常返回给前端
raise ValidationError('书名跟出版社名字不能一致')
else:
# 3 校验成功后返回数据
return attrs