首页 > 其他分享 >drf-Response drf-request.data 序列化类的使用 反序列化新增、修改、删除数据

drf-Response drf-request.data 序列化类的使用 反序列化新增、修改、删除数据

时间:2023-01-17 20:22:53浏览次数:40  
标签:self request book 序列化 data drf

目录

APIView基本使用

# drf是什么?
是一个第三方的app(需要在settings中注册),只能在djagno上使用

# APIView
安装了drf后,就可以导入一个视图类APIView,所有后期要使用drf写视图类,都是继承APIView及其子类。(之前写视图类是继承django的View类)

# 示例
接下来使用APIView写获取所有图书的接口

使用原生Django写接口(View + JsonResponse)

建表:
image-20230115132936382

继承Django View写视图类:
image-20230115133424493

这里可以使用JsonResponce进行序列化吗?
不能。JsonResponse只能序列化一些基础的数据类型如字典、列表。queryset对象不能直接进行序列化,只能通过for循环一个个拼成列表套字典的形式。
image-20230115133737170

视图类路由配置:记得as_views()加括号

但是这样还是会报错:

image-20230115133946493

查看源码:
image-20230115134018999

可知我们上传的不是字典时,会报错。所以我们进行修改:
image-20230115134324191

为了使返回给浏览器的json字符串可以显示中文,还需要加一个参数:(不处理也没事,只是便于查看)

image-20230115134530578

代码:

class BookView(View):
    def get(self, request):
        print(type(request))
        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 JsonResponse(res_list,safe=False,json_dumps_params={'ensure_ascii':False})  # 只能序列化字典和列表,

使用drf写接口(APIView + drf Response)

模块导入:

# 不要忘了注册rest_framework这个app

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

'''
drf 帮我们封装了请求类、响应类、路由类、视图类
模块导入的时候很规范,比较好找
'''

drf 两种导入View的方式

APIView继承自django的View:
image-20230117135831946
两种导入View的方式:

from django.views.generic import View  # View真正的位置
from django.views import View # 通过导包的方式导入View
'''
备注:导入包会执行包内的__init__文件。
'''

drf Response

在原有代码的基础上做改动,修改JsonResponse为drf的Response:
image-20230117140520062

drf的response不仅可以序列化字典列表,还可以序列化字符串。

但是这样修改之后运行程序会报错:模板不存在

image-20230117140649293

这很奇怪,我们一直都是编写视图,都没有用到模板,怎么会报错呢?但是使用postman访问我们的接口,是没有问题的。

问题解决:
image-20230117140846785

注册drf app之后访问图书查询接口:
image-20230117140932560

注册drf app之后用浏览器访问,可以显示一个好看的页面,包括js代码,而不仅仅是json格式的数据。

代码:

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)

APIView源码分析

详细见上一篇博客...

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

# 一旦继承了APIView入口
	-路由配置跟之前继承View是一样的----》找视图类的as_view---》【APIView的as_view】
        @classmethod
        def as_view(cls, **initkwargs):
            # 又调用了父类(View)的as_view
            view = super().as_view(**initkwargs)
            '''
            # 从此以后,所有的请求都没有csrf的校验了
            # 在函数上加装饰器
            @csrf_exempt
            def index(request):
                pass
            本质等同于 index=csrf_exempt(index)
            '''
            return csrf_exempt(view)
        
    -请求来了,路由匹配成功会执行 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 在执行三大认证和视图类的方法过程中只要报错,都会被捕获处理
       

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中取,跟之前一样
    
    
    
# 验证 原生requets.POST 只有urlencoded和form-data格式提交的数据,json格式提交的数据在body中,需要拿出来自己处理,但是drf的request中有个data,data中可以取到任意编码提交的数据
    
# request.data  有时候是(urlencoded,form-data)QueryDict,有时候(json)是字典
    
# 4 什么是魔法方法?
    1 在类中只要以__开头,__结尾的都称之为魔法方法
    2 这种方法不需要手动调用,某种情况会自动触发
    3 你学过的: __init__,__str__,__call__,......

drf request.data

当用户上传文件时,原生django的request不允许你print(request.body):
image-20230117150115359

原因:如果用户传5个G的文件,当文件太大时无法打印。

使用drf的request,无论任何编码格式,都可以进行打印操作:

image-20230117150630728

form-data、urlencode格式:
image-20230117150527320
上传Json格式数据:
image-20230117150727955

注意:request.data的结果是python字典,而不是querydict。

FBV实现request.data方法(练习)

练习:写一个装饰器装饰FBV,只要一装饰,该视图函数中的request就会有request.data这个方法。无论发送到后端的数据是哪种编码格式,这个request.data里面都有数据。

实现逻辑:

1.如果传入json:
	从request.body中将json数据取出,再使用json.loads反序列化转成字典赋值给request.data.
2.如果是urlencoded或form-data格式:
	直接把request.data = request.POST
    
'''如何判断传入后端数据的编码格式?'''

序列化组件介绍

# 序列化组件介绍
是drf提供了一种可以快速实现序列化的类:序列化类 
我们也可以继承它,写自己的类。
使用方式类似form组件。

# 用途
用来序列化queryset查询集合或单个对象的

# 原来的序列化方式
写一个获取所有图书接口 ---> 查询得到qs,单个book对象--->使用for循环,取出数据拼接成列表套字典--->转成json格式字符串,给前端 ---> 序列化

# 序列化组件与form组件对比 (重要)
form类中写属性       --->  对类中出现的属性进行校验
serializer类中写属性 --->  对类中出现的属性进行序列化 

'''
serializer类中写到的属性 --对应--> 数据库中某字段,只有这些字段才会被序列化(类似form组件)
其他字段将不会出现在序列化对象的data方法中
'''

image-20230117200452171

序列化类中只有name属性时,查询所有图书的结果:只会对name字段进行序列化。

image-20230117170541304

有三个属性时,查询所有图书:

image-20230117170658940

定义一个序列化类

首先需要在app下,新建一个py文件,我们将其命名为serializer:
image-20230117152913464

然后需要继承drf写好的序列化类。
注意继承的方式,这么写是因为类中还要使用序列化组件中的字段类。

# 写序列化类:给book进行序列化
# from rest_framework.serializers import Serializer
from rest_framework import serializers


class BookSerializer(serializers.Serializer):
    # 要序列化的字段    有很多字段类,字段类有很多字段属性
    name = serializers.CharField()  # 字段类CharField
    # 这个CharField和model层、form组件的CharField不是一个东西。
    # price = serializers.CharField()
    publish = serializers.CharField()

序列化类的使用

序列化多条数据

原来的代码:
image-20230115134530578

修改原来的BookView的代码,将for循环的部分替代掉,让序列化类帮助我们处理数据:

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)  # 使用drf response
    
 '''instance:对象'''

这个instance参数可以传入queryset查询集(多个对象),也可以传入first取出来的单个对象。
当instance传入单个书籍对象时,可以不写many=True(默认情况many=False),
当传入queryset对象时,一定要传many=True

序列化单条数据

由于获取单条数据、多条数据都使用的是get请求,所以都是执行我们视图类中的一个get方法。两个不同的业务,都使用一个类中函数来处理,不太合适,有点臃肿:
image-20230117160050548

所以我们选择新创建一个视图类,来实现查询一条数据的需求:

# 查询单条数据时的路由:/book/1/
class BookDetailView(APIView):
    def get(self, request, pk):  # 有名分组传入pk
        book = Book.objects.filter(pk=pk).first()  # 获取book对象
        ser = BookSerializer(instance=book)  # 无需 many=True
        return Response(ser.data)

修改路由层:

image-20230117160746279

反序列化

# 反序列化
后端发送给前端  --->  序列化
前端发送给后端  --->  反序列化

# 涉及序列化的操作
新增,修改---》前端传入的数据,要校验---》序列化类有数据校验功能

'''删除不涉及到序列化'''

新增数据

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)

新增数据基本流程

前端传入的数据在request.data里:
image-20230117173643240

通过双星号解包使用create方法新增数据。但是这样是有问题的,因为没有进行数据校验,不知道前端传入的数据是否合法。
此时可以选择form组件进行校验,但是序列化组件也有校验功能。

将前端输入传入序列化类的data参数,实例化出一个对象:
image-20230117174030816

序列化对象也有is_valid方法(这里我们没有写校验规则,只是走一下流程):
image-20230117174325209

校验通过进行保存。

依据resful规范,新增数据时,要返回新增的数据对象:
image-20230117174512125

所以校验成功时,应该返回新增的对象;不成功时,返回错误信息。

序列化对象save方法报错

使用postman发送POST请求:
image-20230117174707103

会产生报错:
image-20230117174746995
提示create方法需要被重写(implemented)。

问题的原因:序列化对象的save方法
image-20230117175002813

由于我们写的BookSerializer中没有save方法,所以会去父类serializers找save方法。

save方法中看这一部分代码:
image-20230117175804130
由于我们是发送POST请求,所以序列化对象中没有instance这个属性(get请求中有instance属性,post请求有data属性),所以这里会执行self.create

又因为我们的BookSerializer中没有create方法,故又去父类找:
image-20230117180115334
父类的create方法会直接抛出异常,提醒你重写create方法。
所以执行序列化对象.save()会报错。

重写序列化类create方法

在自己的序列化类重写create方法:

image-20230117180441311

这里需要注意的是,要将新增的书籍对象给返回出来:
image-20230117180719496 否则会触发断言。

代码:

class BookSerializer(serializers.Serializer):
    # 要序列化的字段    有很多字段类,字段类有很多字段属性
    name = serializers.CharField()  # 字段类
    price = serializers.CharField()
    publish = serializers.CharField()

    # 重写create方法,
    def create(self, validated_data):
        res = Book.objects.create(**validated_data)
        return res

修改数据

视图类:

# /book/1/ 修改主键为1的书籍
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)

修改数据使用put请求访问路由book/1/
修改数据需要给序列化类传入两个参数:instance、data
也就是用前端传入的数据,修改后端查询到的数据。

这里序列化类的save方法也会报错,原因是:

image-20230117194751524

当instance有值时,会执行序列化对象的update方法,这个方法也需要我们在序列化类中重写。

重写序列化类updata方法

class BookSerializer(serializers.Serializer):
    # 要序列化的字段    有很多字段类,字段类有很多字段属性
    name = serializers.CharField()  # 字段类
    price = serializers.CharField()
    publish = serializers.CharField()

    # 重写 update
    def update(self, instance, validated_data):
        # instance 要修改的对象(传入的queryset\模型对象)
        # validated_data 校验过后的数据
        instance.name = validated_data.get('name')
        instance.price = validated_data.get('price')
        instance.publish = validated_data.get('publish')
        instance.save()
        return instance

这里需要注意的是:

  1. 需要调用save方法,将instance对象中的数据,存储到数据库。
  2. 需要将instance对象返回出去,否则会触发断言。
    (resful规范:修改之后,将修改成功的对象返回)

删除数据

直接在CBV中添加delete方法:

image-20230117195554977

依据resful规范:删除数据应该返回一个空文档,所以这里返回一个Response()

练习

1 继承apiview写5个接口
2 fbv写个装饰器,装饰在视图函数,只要一装饰,以后的request就可以使用request.data,这个data无论是那种编码格式,都有数据

标签:self,request,book,序列化,data,drf
From: https://www.cnblogs.com/passion2021/p/17058643.html

相关文章

  • Java反序列化-CommonsCollections1利用链分析
    前言学习Java基础语法也有1年多的时间了,Java安全基础也学了有半年了,期间通过ctf赛题学习过fastjson的反序列化并了解了其利用链,但并未深入学习并记录笔记。一直都说要赶......
  • Spring的OncePerRequestFilter 过滤器
    Spring的OncePerRequestFilter原文链接:https://blog.csdn.net/weixin_43944305/article/details/119923969Spring的OncePerRequestFilterOncePerRequestFilter顾名......
  • Springboot之OncePerRequestFilter 过滤器
    Springboot之OncePerRequestFilter过滤器原文链接:https://www.cnblogs.com/javalinux1/p/16389683.html类说明OncePerRequestFilter能够确保在一次请求只通过一次filte......
  • 4.Requests【HTTP协议】响应
    一、前言当然,有请求就有响应,浏览器在接收到服务端的响应数据后,经过处理将有用的信息展示到浏览器上。所以,接口自动化也需要对响应内容进行解析或提取有用的数据。二、学......
  • 5.Requests【接口请求】requests模拟发送get请求
    一、前言在前面的课程中我们讲了fiddler的使用,其实对应接口自动化来说,只需要知道怎么通过inspectors页签获取接口信息即可,关于fiddler的其他使用在接口测试中才会使用。......
  • 6.Requests【接口请求】requests模拟发送post请求
    一、前言上节课我们讲到了如何通过requests模块模拟发送get请求,我们这节课学习一下另一个常见的http请求方式:post请求。与get不同的是,post请求可以传请求体,而get没有请求......
  • 7.Requests【接口请求】POST请求的data格式请求体传参
    一、前言我们上节课简单学习了只有url的post请求,但是在实际场景中,与get请求不同的是post请求是有请求体的,数据除了请求方发送之外,还要服务端能够解析才有意义。而这个解析......
  • 8.Requests【接口请求】POST请求的json格式请求体传参
    一、前言我们上节课已经学习了Content-Type:application/x-www-form-urlencoded时的传参方式,可以用data来传参。还有一种常见的接口数据传输类型,application/json格式,顾......
  • 9.Requests【接口请求】POST请求的files格式请求体传参(文件上传)
    一、前言前面的课程中已经学习了两种POST请求中文件的传输格式,还有一种格式,Content-Type:multipart/form-data时的传参方式,这种数据传输方式的接口一般是文件上传接口,可......
  • 11.Requests【接口请求】requests模拟headers传参
    一、前言我们在讲http协议的时候,已经了解到接口的请求存在请求体,也叫header。其实在模拟接口请求时,如果后台没有对header中的参数做校验,则可以不用传header信息,比如我们上......