APIView与Request类的源码分析、序列化组件

120 阅读6分钟

1.APIView基本使用

# drf:是一个第三方的app,只能在djagno上使用
# 安装了drf后,导入一个视图类APIView,所有后期要使用drf写视图类,都是继承APIView及其子类

eg:编写获取所有图书的接口

1.1使用View+JsonResponse写

路由设计:path('books/', BookView.as_view()),


class BookView(View):
    def get(self, request):
        book_list = Book.objects.all()
        # book_list是一个queryset对象 不能直接序列化 需要通过for循环取出一个个的字典,再添加到列表里,再交给JsonResponse去序列化
        res_list = []
        for book in book_list:
            res_list.append({'name': book.name, 'price': book.price, 'publish': book.publish})
        return JsonResponse(res_list, safe=False, json_dumps_params={'ensure_ascii': False})  # JsonResponse只能序列化列表或者字典数据

1.2使用APIView+drf的Response写

class BookView(APIView):  # APIView继承自django的View
    def get(self, request):
        book_list = Book.objects.all()  # queryset不能直接序列化
        res_list = []
        for book in book_list:
            res_list.append({'name': book.name, 'price': book.price, 'publish': book.publish})
        return Response(res_list)  # drf的Response序列化返回时不用设置ensure_ascii参数,会自动转换中文格式

2.APIView源码分析

# 视图类继承APIView后 执行流程就发生了变化 这个变化就是整个drf的执行流程
路由配置跟之前继承View是一样的----》找视图类的as_view---》【APIView的as_view】

第一步:点进as_view
@classmethod
def as_view(cls, **initkwargs):  
    # 由于我们的视图类BookView继承的是APIView 所以这里的as_view是 APIView 的as_view
    view = super().as_view(**initkwargs)  # 又调用了父类(View)的as_view
    return csrf_exempt(view)  # 这句话帮我们取消了csrf的校验
'''
补充:在函数上加装饰器
@csrf_exempt
def index(request):
	pass
本质等同于  index = 	csrf_exempt(index)
所以我们 return csrf_exempt(view)  就等同于 return view(此时的view被csrf_exempt装饰 就取消了csrf的校验)
'''

-请求来了,路由匹成功之后会执行View类的as_view类方法内的view闭包函数(但是取消了csrf认证)
-真正的执行:执行self.dispatch---->APIView的dispatch  【这是重点】

第二步:点进dispatch  #(这里的点进dispatch是APIView的点进dispatch)
    def dispatch(self, request, *args, **kwargs):  
        # 参数里的request是django原生的request
        # 下面的request 变成了drf提供的Request类产生的request对象
        request = self.initialize_request(request, *args, **kwargs)
        # def initialize_request(self, request, *args, **kwargs):
        #    ......
        #    return Request()
        self.request = request
        # 这里的self是我们视图类BookView的对象  上面这句话的作用是将新的request赋值给我们原生的request
        try:
            self.initial(request, *args, **kwargs) # 执行了权限、频率、权限
            if request.method.lower() in self.http_method_names: 
                # 原生的View的dispatch的东西
                handler = getattr(self, request.method.lower(),
                                  self.http_method_not_allowed)
            else:
                handler = self.http_method_not_allowed
            response = handler(request, *args, **kwargs)
        except Exception as exc:
            # 在执行三大认证或者视图类的方法的过程中出现异常,那就捕获并处理异常 并正常返回
            response = self.handle_exception(exc)
        self.response = self.finalize_response(request, response, *args, **kwargs)
        return self.response

 总结:
	1.只要视图类继承了APIView就都没有csrf的认证了
	2.以后视图类中使用的request对象,都已经变成drf提供的Request类的对象了
	3.执行视图类的方法之前,执行了三大校验(认证、权限、频率)    
	4.在执行三大认证和视图类的方法中只要报错就都会被捕获异常

3.Request类源码分析

1. 视图函数中使用的request对象,已经变成了drf提供的Request类的request对象了
	原生的django的request:
	class BookView(View):
        def get(self, request):
            print(type(request))  # <class 'django.core.handlers.wsgi.WSGIRequest'>
	drf的request:   
    class BookView(APIView):
        def get(self, request):
            print(type(request))  # <class 'rest_framework.request.Request'>

2. 新的request依旧能使用老的request的方法
	print(request.method)  # get
	print(request.path)  # /books/
	print(request.GET)   # 原来的get请求提交的参数
	print(request.POST)  # 原来post请求提交的参数

3.Request源码分析:    rest_framework.request.Request
# 补充:__getattr__: 对象.属性时,该属性不存在会自动触发该方法的执行

class Request:
    def __getattr__(self, attr):  # 如果取的属性不存在就会去原生django的request中取出来。也就是说drf的request对象使用的GET、POST...全是原生django提供的。
        try:
            return getattr(self._request, attr) 
            # self._request指的就是django原生的request
        	# 反射  根据字符串获取属性或方法,
        except AttributeError:
            return self.__getattribute__(attr)

-以后想使用的所有属性或方法直接用就可以 --->drf会通过反射区原来的request中取
-新的request内部有个老的request 就是request._request
-data是个方法,被property装饰了 ,编程了数据属性
4.数据获取:
    1.post请求的数据:
        -原生django:urlencoded,form-data格式的数据在request.POST中取,json格式数据在request.body中取
        -drf:全部去request.data中取
    2.get请求的数据: 
		-原生的django:request.GET中取
		-drf:request.query_params  或  request._request.GET  或  request.GET
         # 其实request._request指的就是原生的request
	3.文件数据:都是从request.FILES中取,跟之前一样
    

    # 注意:
    	当编码格式是urlencoded,form-data时 request.data是 'QueryDict'
        当编码格式是json时 request.data是 '普通字典'

4.序列化组件(查询所有、查询单个)

介绍:序列化组件就是drf提供的一个类,我们可以继承他,写自己的类
作用:序列化queryset或者单个对象(qs.first())

4.1:定义一个序列化类

serializer.py:

from rest_framework import serializers
class BookSerializer(serializers.Serializer):
    # 这里写要序列化的字段 如果将字段注释即为取消序列化
    name = serializers.CharField()  
    price = serializers.IntegerField()
    publish = serializers.CharField()

4.2:使用序列化类,序列化多条数据

class BookView(APIView):  # APIView继承自django的view
    def get(self, request):  # 获取多条数据用get请求
        book_list = Book.objects.all()
        ser = BookSerializer(instance=book_list, many=True)
        # instance表示要序列化的QuerySet对象,many=True表示要序列化多条数据 这两个参数一定要传
        return Response(ser.data)

4.3:序列化单条数据

路由设计: path('books/<int:pk>', BookDetailView.as_view()),

class BookDetailView(APIView):
    def get(self, request, pk):  # 查询单本图需要传id
        book = Book.objects.filter(pk=pk).first()
        ser = BookSerializer(instance=book)  # 单条数据不需要指定many=True
        return Response(ser.data)

5.反序列化(新增、修改)

5.1:序列化类编写

增加和修改数据需要重写create和update方法

class BookSerializer(serializers.Serializer):
    # 这里写要序列化的字段 如果将字段注释即为取消序列化
    name = serializers.CharField()
    price = serializers.IntegerField()
    publish = serializers.CharField()

    # 重写create方法
    def create(self, validated_data):  # validated_data 校验过后的数据
        res = Book.objects.create(**validated_data)
        return res

    # 重写update方法
    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')
        instance.save()
        return instance

5.2:新增

class BookView(APIView):
    def post(self, request):  # 新增用post请求
        # 前端传入的数据 从request.data中取出来
        ser = BookSerializer(data=request.data)
        if ser.is_valid():  # 如果校验通过则完成新增。只不过我们这里没有写校验规则
            ser.save()  # 调用save会触发BookSerializer的save方法,判断了,如果instance有值执行update,没有值执行create,但是我们目前的序列化类没有这两个方法。我已我们需要重写这两个方法,不然这里会报错。
            return Response(ser.data)
        else:
            return Response(ser.errors)

5.3:修改

class BookSerializer(serializers.Serializer):
	def put(self,request,pk):  # 修改用put请求
        book=Book.objects.filter(pk=pk).first()
        # instance有值表示修改
        ser = BookSerializer(instance=book, data=request.data)
        if ser.is_valid():
            ser.save()
            return Response(ser.data)
        else:
            return Response(ser.errors)

5.4:删除

class BookDetailView(APIView):
    def delete(self, request, pk):
        Book.objects.filter(pk=pk).delete()
        return Response()

作业

1 继承apiview写5个接口()
2 fbv写---》写个装饰器,装饰在视图函数,只要一装饰,以后的request就可以使用request.data,这个data无论是那种编码格式,都有数据
1 继承apiview写5个接口()
class BookView(APIView):
    def get(self, request):  # 查所有
        book_list = Book.objects.all()
        res_list = []
        for book in book_list:
            res_list.append({'name': book.name, 'price': book.price, 'publish': book.publish})
        return Response(res_list)

    def post(self, request):  # 增
        res_dict = {}
        for key in request.data:
            res_dict[key] = request.data.get(key)
        print(res_dict)
        Book.objects.create(**res_dict)
        return Response(res_dict)


class BookDetailView(APIView):
    def get(self, request, pk):  # 查一本
        res_dict = {}
        book = Book.objects.filter(pk=pk).first()
        res_dict['name'] = book.name
        res_dict['price'] = book.price
        res_dict['publish'] = book.publish
        return Response(res_dict)

    def put(self, request, pk):  # 改
        res_dict = {}
        for key in request.data:
            res_dict[key] = request.data.get(key)
        Book.objects.filter(pk=pk).update(**res_dict)
        return Response(res_dict)

    def delete(self, request, pk):  # 删
        Book.objects.filter(pk=pk).delete()
        return Response()
2 fbv写---》写个装饰器,装饰在视图函数,只要一装饰,以后的request就可以使用request.data,这个data无论是那种编码格式,都有数据
def outer(func_name):
    def inner(request, *args, **kwargs):
        if request.POST:
            request.data = request.POST
        if request.body:
            request.data = json.loads(request.body)
        res = func_name(request, *args, **kwargs)
        return res
    return inner


@outer
def book_view(request):
    print(request.data)
    return HttpResponse()