rest-framework之APIView基本使用及源码分析、Request类源码分析、序列化组件介绍及基本使用、反序列化

安装djangorestframework

方式一:pip3 install djangorestframework
方式二:pycharm图形化界面安装
方式三:pycharm命令行下安装(装在当前工程所有的解释器下)

APIView基本使用

drf是一个第三方的app,只能在django上使用,,安装了drf后,导入一个视图类APIView(继承在django中的view),所有后期要使用drf写视图类,都是继承APIView及其子类
drf封装了请求类、响应类...,比较规范

使用View+JsonResponse写

from django.http import JsonResponse
from django.shortcuts import render

# Create your views here.
from django.views import View

from app01.models import Book


class BookView(View):
    def get(self, request):
        print(type(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 JsonResponse(res_list, safe=False, json_dumps_params={'ensure_ascii': False})

image

使用APIView+drf的Response写

注意:要注册rest_framework这个app,不注册就会报错

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.request import Request

class BookView(APIView):  # APIView继承自django的View
    def get(self, request):
        # print(type(self.request))  # 新的request
        # print(type(request))
        print(request._request)
        print(type(request._request)) #django.core.handlers.wsgi.WSGIRequest

        # print(request.method)  # get
        # print(request.path)  # /books/
        # print(request.GET)  # 原来的get请求提交的参数
        # print(request.POST)  # 原来post请求提交的参数

        book_list = Book.objects.all()
        # book_list是queryset对象不能直接序列化,只能通过for循环一个个拼成列表套字典的形式
        res_list = []
        for book in book_list:
            res_list.append({'name': book.name, 'price': book.price, 'publish': book.publish})
        return Response(res_list)

image

class BookView(APIView)---> APIView继承自django的view
image

image

from django.views.generic import View
from django.views import View
这俩个是一样的,后者在__init__.py文件中注册一下,就是将generic里面的复制了一份出来

image

APIView源码分析

视图类继承APIView后,执行流程就发生了变化,这个变化就是整个的drf的执行流程

一旦继承了APIView入口
    -路由配置跟之前继承View是一样的----》找视图类的as_view---》【APIView的as_view】
    @classmethod
    def as_view(cls, **initkwargs):
        # 又调用了父类(View)的as_view
        view = super().as_view(**initkwargs)
            ...
        return csrf_exempt(view)

image

校验csrf的问题

1.最开始我们是直接注释掉关于crsf的那个中间件
2.在函数上加装饰器,@csrf_exempt
'''
    @csrf_exempt
    def index(request):
        pass
'''
    本质等同于 index=csrf_exempt(index)
3.现在,return csrf_exempt(view),但是前提是必须继承了APIView这个类,就不需要去校验csrf了

image

-请求来了,路由匹配成功会执行 View类的的as_view类方法内的view闭包函数(但是没有了csrf认证),
-真正的执行,执行self.dispatch---->APIView的dispatch  【这是重点】
 def dispatch(self, request, *args, **kwargs):
    # 参数的request是原来的django原生的request
    # 下面的request,变成了drf提供的Request类的对象---》return Request(。。。)
    request = self.initialize_request(request, *args, **kwargs)
    # self 是视图类的对象,视图类对象.request=request 新的request
    self.request = request
    try:
        # 执行了认证,频率,权限 [不读]
        self.initial(request, *args, **kwargs)
        # 原来的View的dispatch的东西
        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
        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 执行视图类的方法之前,执行了3大认证(认证,权限,频率)
    4 在执行三大认证和视图类的方法过程中只要报错,都会被捕获处理

image

Request类源码分析(验证以后视图类中使用的request对象,已经变成了drf提供的Request类的对象了)

1 视图类中使用的request对象,已经变成了drf提供的Request类的对象了

原生djagno 的request是这个类的对象:
    django.core.handlers.wsgi.WSGIRequest
drf的request是这个类的对象:
    rest_framework.request.Request

2 request已经不是原来的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__    对象.属性,属性不存在会触发它的执行

def __getattr__(self, attr): # 如果取的属性不存在会去原生django的request对象中取出来
        try:
            #反射:根据字符串获取属性或方法,self._request 是原来的request
            return getattr(self._request, attr)
        except AttributeError:
            return self.__getattribute__(attr)

    -以后用的所有属性或方法,直接用就可以了--->(通过反射去原来的request中取的)

    -新的request内部有个老的request,就是 request._request

    -data 是个方法,被property装饰了,变成了数据属性用
        -以后body体中提交的数据,都从这里取(request.POST)
        -urlencoded,form-data:提交的数据在request.POST中
        -json格式提交的数据,在requets.POST中没有,它在request.body中
        -现在无论那种格式,都从request.data中取
    -query_params:get请求提交的参数,等同于request._request.GET 或  request.GET
    -其他:取文件也是从request.FILES中取,跟之前一样

4 验证原生requets.POST

验证原生requets.POST 只有urlencoded和form-data格式提交的数据,json格式提交的数据在body中,拿出来自己处理,但是drf的request中有个data,data中可以取到任意编码提交的数据

form-data格式提交的数据

image

image

urlencoded格式提交数据

image

image

json格式提交数据

image

json格式提交的数据在body中
image
request.data 有时候是(urlencoded,form-data)QueryDict,有时候(json)是字典

image

序列化组件介绍

序列化组件是什么?

drf提供的一个类,我们继承它,写自己的类

它有什么用?

前后端通常使用json数据结构交互,在后端我们也想把一个对象返回给前端,但是json序列化不能序列化对象的,所有就有了序列化组件,通过自定义特定结构将对象返回给前端,同时可以对前端传入的数据进行数据的校验等功能。

序列化组件基本使用

定义一个序列化类,在app下新建一个serializer.py文件

from rest_framework import serializers


class BookSerializer(serializers.Serializer):
    name = serializers.CharField()
    price = serializers.CharField()
    publish = serializers.CharField()
    

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

class BookView(APIView):  # APIView继承自django的View
    def get(self, request):
        book_list = Book.objects.all()
        # instance表示要序列化的数据,many=True表示序列化多条instance是qs对象,一定要传many=True
        ser = BookSerializer(instance=book_list, many=True)
        return Response(ser.data)

image

使用序列化类,序列化单条数据

class BookDetailView(APIView):
    def get(self, requset, pk):
        book = Book.objects.filter(pk=pk).first()
        ser = BookSerializer(instance=book)
        return Response(ser.data)

image

反序列化

新增数据进去

class BookView(APIView):  # APIView继承自django的View
    def post(self, request):
        # 前端传递数据,从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)

image

修改数据

class BookDetailView(APIView):
    def put(self, request, pk):
        book = Book.objects.filter(pk=pk).first()
        # 前端传递数据,从request.data取出来
        ser = BookSerializer(instance=book, data=request.data)
        if ser.is_valid():  # 表示校验前端传入的数据   没有写校验规则,现在等于没校验
            ser.save()  # 再写东西,这里会报错  调用save会触发BookSerializer的save方法,判断了,如果instance有值执行update,没有值执行create
            return Response(ser.data)
        else:
            return Response(ser.errors)

image
删除数据

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

image

序列化类

from rest_framework import serializers

from app01.models import Book


class BookSerializer(serializers.Serializer):
    name = serializers.CharField()
    price = serializers.CharField()
    publish = serializers.CharField()

    def create(self, validated_data):
        res = Book.objects.create(**validated_data)
        return res

    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

作业(无论什么编码格式都从request.data中取)

fbv写--->写个装饰器,装饰在视图函数,只要一装饰,以后的request就可以使用request.data,这个data无论是那种编码格式,都有数据

def func_name(func):
    def inner(request, *args, **kwargs):
        if request.POST:
            request.data = request.POST
        else:
            request.data = json.loads(request.body)
        res = func(request, *args, **kwargs)
        return res

    return inner


@func_name
def index(request):
    print('data:', request.data)
    print(request.POST)
    return HttpResponse('ok')

image

posted @ 2022-09-26 18:56  张张包~  阅读(155)  评论(0编辑  收藏  举报