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 @ 2023-02-01 21:56  Duosg  阅读(64)  评论(0编辑  收藏  举报