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)APIViewas_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)方法,相当于执行了Viewas_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
posted @   Duosg  阅读(71)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
点击右上角即可分享
微信分享提示