DRF从入门到精通二(Request源码分析、DRF之序列化组件)

news2024/9/20 20:35:07

文章目录

  • 一、Request对象源码分析
    • 区分原生request和新生request
    • 新的request还能像原来的reqeust一样使用吗
    • 源码片段分析
    • 总结:
  • 二、DRF之序列化组件
    • 序列化介绍
    • 序列化步骤
    • 序列化组件的基本使用
    • 反序列化基本使用
      • 反序列化的新增
      • 反序列化的新增
      • 删除单条
    • 反序列化的校验

一、Request对象源码分析

在上一篇博客中最后分析APIView时,我们分析出继承了APIView的视图,以后request都是新的request了,是DRF提供的Request的对象

区分原生request和新生request

	原生request:django.core.handlers.wsgi.WSGIRequest

	新生request:from rest_framework.request import Request

	原生request可以在新生request._request中获取

新的request还能像原来的reqeust一样使用吗

	用起来屎一样的,新的request只是在原来的request基础上添加了一些功能,并不影响基础功能的使用,其本质就是类似装饰器
	 print(request.method)  # get
     print(request.path)  # /movies/
     print(request.GET)   # 原来的get请求提交的参数
     print(request.POST)  # 原来post请求提交的参数

源码片段分析

	'''源码解析之 __init__'''
	从上面区分原生request和新的request中可以知道,老的request对象其实就是在新的内部,request._request中
	'我们先看__init__,它是一个初始化方法,类实例化得到对象时,会执行它,然后会往对象中放数据'
	
	def __init__(self, request, parsers=None, authenticators=None,
                 negotiator=None, parser_context=None):
		
		'''
		传入的request是老的,django原生的request
		放到self._request,self是新的request类的对象
		这里它把传入过来的原request放到这个self._request中,这里的self已经不是视图类了,
		因为这个Request类没有继承任何一个类,它就是它自己,所以这个self是Request
		'''
        self._request = request
        self._data = Empty
        self._files = Empty
        .....

在类内部,以 __开头 __结尾的方法, 在某种情况下会自动调用,他们称之为魔法方法。具体有那些,可以自行搜索,因为所有的类都继承了Object类,所以也可以在Object类中看看,但是不全里面

在这里插入图片描述

	'''源码解析之__getattr__'''
	类中有个魔法方法:__getattr__,对象.属性,属性不存在会触发它的执行
   def __getattr__(self, attr): 如果取的属性不存在会去原生的Django的request对象中取出来
	   try:
 '通过反射,因为这里是self._request所以去Django的request取,能取到就返回,娶不到就执行except代码,如果还取不到则报错'
	        return getattr(self._request, attr)
	    except AttributeError:
	        return self.__getattribute__(attr)
	 '以后用的所有属性或方法,直接用就可以了(通过反射去原来的request中取)' 

以后新的request中多了一个属性data,它会把前端post/put提交的请求体中的数据,都放在request.data中,无论何种编码格式,它都是字典

	data是一个方法,被property装饰了,变成了数据属性用
		-以后body中提交的数据,都从这里取(request.POST)
		-urlencoded,form-data:提交的数据在request.POST中
		-json格式提交的数据,在request.POST中是没有的,它在request.body中
		-现在无论那种格式,都可以直接从request.data中取
	
	request.query_params:get请求提交的参数,以后从这里取
	
	request.FILES:取文件就还是从这个里面取,和之前一样

总结:

	1.新的request跟之前的用法一模一样,如果新的request取不到,它使用__getattr__魔法方法去原生request中取。
		当然原生的也可以直接在新的request中拿到,request._request
	2.新的request中多了data属性,request.data客户端提交的请求体中的数据,无论是什么编码都在request.data中
	3.其他的使用和原生的request一模一样
		request.query_params就是原来的request._request.GET
		上传的文件从request.FILES

	
	'''
	1.原生Django提交数据(post),只能处理urlencoded和form-data编码,从request.POST中取
	2.原生Django提交数据(put),处理不了,需要我们从request.body中取出来进行处理
		分不同编码格式:
			urlencoded------》例:name=lqz&age=19---》使用字符串切割的方式.split
			json----->{'xxx':'xx','yyy':'yy'}--->需要自己进行json.loads反序列化
	3.原生Django不能处理json提交的数据(post/put),需要自己做反序列化
		json----->{'xxx':'xx','yyy':'yy'}--->需要自己进行json.loads反序列化
	4.新的request解决了所有问题:request.data
	'''

二、DRF之序列化组件

	序列化类(组件)可以做的事情:
		1.序列化,QuerySet对象,单个对象做序列化给前端
		2.反序列化数据校验:前端传入数据后校验数据是否合法
		3.反序列化数据保存:前端传入数据,存到数据库中

序列化介绍

  • 在写接口时,需要序列化和反序列化,而且反序列化的过程中要做数据校验,drf直接提供了固定的写法,只需要按照固定写法,只需要按照固定写法使用,就能完成上面的三个需求。
  • 提供了两个类SerializerModelSerializer,编写自定义的类,只需要继承drf提供的序列化类,就可以使用其中的某些方法,也能完成上面的三个需求

序列化类的作用:做序列化、反序列化、反序列化校验

序列化步骤

	1.写一个py文件,叫serializer.py(命名随意)
	2.写一个序列化类,继承serializers.Serializer,
	3.在类中编写需要序列化的字段
		例:name=serializers.CharField()
	4.在视图类中使用,导入models文件中的类books,然后实例化得到对象,对查出来的query对象们,
		对单个对象序列化并传入instance=books参数
		如果query是复数,一定要串many=True,如果query是单个对象,就无需传入many
	5.序列化类对象:ser.data---->字典或列表----->通过Response将json格式字符串返回给前端

序列化组件的基本使用

1.创建一个py文件 ----》serializer.py
	from rest_framework import serializers
	
	class BookSerializer(serializers.ModelSerializer):
	    name = serializers.CharField(max_length=18, min_length=2, required=True)
	    price = serializers.IntegerField(required=True)
	    
	    class Meta:
	        model = Book
	        fields = '__all__'
	        

2.view.py文件中
	from app01 import models
	from rest_framework.views import APIView
	from rest_framework.response import Response
	from .serializer import BookSerializer
	
	class BookView(APIView):
	    def get(self, request):
	        book_list = models.Book.objects.all()
	        ser = BookSerializer(instance=book_list, many=True)  # 序列化多条需要many=True
	        return Response({'code': 100, 'msg': '查询成功', 'results': ser.data}) # 无论是列表还是字典都可以序列化

	class BookDetailView(APIView):
	    def get(self, request, pk):
	        book_obj = models.Book.objects.filter(pk=pk).first()
	        ser = BookSerializer(instance=book_obj)
	        if ser.is_valid():
	            return Response({'code': 100, 'msg': '查询一条成功', 'results': ser})
	        else:
	            return Response(ser.errors)

3.urls.py文件中
	urlpatterns = [
	    path('books/', views.BookView.as_view()),
	    path('books/<int:pk>', views.BookDetailView.as_view()),
	]

在这里插入图片描述

反序列化基本使用

反序列化过程:新增、修改

	新增:
        1. 前端传入后端的数据,不论编码格式,都在request.data中,request.data格式是字典
		   前端根据传入的编码格式不一样,从request.data取到的字典形式也是不一样的
		      编码格式                   字典
             urlencoded               QueryDict
              form-data                QueryDict
               json                       dict
        2. 将前端传入的数据request.data进行反序列化,并完成序列化类的反序列化
        3. 序列化类得到对象并传入参数:data=request.data
               校验数据
               保存:ser.save()--->序列化类中重写create方法
    
    修改:
        1. 拿到前端传入的数据,进行反序列化,查出要修改的对象--->序列化类的反序列化
        2. 序列化类得到对象,传入参数:instance=要修改的对象,data=request.data
               校验数据 
               保存:ser.save()  --->序列化类中重写update方法

反序列化的新增

序列化类

	class BookSerializer(serializers.ModelSerializer):
	    name = serializers.CharField()
	    price = serializers.IntegerField()
	
	    class Meta:
	        model = Book
	        fields = '__all__'
	
	    # 新增一条数据
	    def create(self, validated_data):
	        # 保存的逻辑
	        # validated_data 校验过后的数据 {name,price,publish}
	        # 保存到数据库
	        book = Book.objects.create(**validated_data)
	        # 一定不要忘记返回新增的对象
	        return book

视图类

	class BookView(APIView):
	    def get(self, request):  # 获取多条数据
	        book_list = models.Book.objects.all()
	        '''instance表示要序列化的数据,many=True表示序列化多条(instance是QuerySet对象)'''
	        ser = BookSerializer(instance=book_list, many=True)  # 序列化多条需要many=True
	        return Response({'code': 100, 'msg': '查询成功', 'results': ser.data})
	
	    def post(self, request):
	        ser = BookSerializer(data=request.data)  # 从前端传递数据从request.data中取出来
	        if ser.is_valid():  # is_valid表示校验前端传入的数据,但是我们没有写校验规则
	            # 保存,需要自己写,要在序列化类BookSerializer中重写create方法
	            ser.save()  # 调用ser.save,自动触发自定义编辑create方法保存数据
	            '''
	            这个时候发送post请求会发生报错,NotImplementedError: `create()` must be implemented.
	            这个时候点击我们点击save查看源码是调用了Save会触发BaseSerializer的方法
			    判断了 如果instance有值执行update,没有值执行create 看到create没有写 所以我们得重写Create
	            '''
	            return Response({'code': 100, 'msg': '添加成功', 'results': ser.data})
	        else:
	            return Response({'code': 101, 'msg': ser.errors})

反序列化的新增

序列化类

	class BookSerializer(serializers.ModelSerializer):
	    name = serializers.CharField()
	    price = serializers.IntegerField()
	
	    class Meta:
	        model = Book
	        fields = '__all__'
	
	    # 修改对象
	    def update(self, instance, validated_data):
	        # instance 要修改的对象
	        # validated_date 校验过后的数据
	        instance.name = validated_data.get('name')
	        instance.price = validated_data.get('price')
	        instance.save()  # orm的单个对象,修改了单个对象的属性,只要调用对象.save就可以修改保存到数据库
	        return instance  # 记得把修改的对象返回

视图类

	class BookDetailView(APIView):
	    def get(self, request, pk):  # 获取单条数据
	        book_obj = models.Book.objects.filter(pk=pk).first()
	        ser = BookSerializer(instance=book_obj)
	        return Response({'code': 100, 'msg': '查询一条成功', 'results': ser.data})
	
	    def put(self, request, pk):
	        book_obj = models.Book.objects.filter(pk=pk).first()
	        ser = BookSerializer(instance=book_obj,data=request.data)
	        if ser.is_valid():
	            ser.save()  # 同新增一样,需要重写update方法
	            return Response({'code': 100, 'msg': '修改一条成功', 'results': ser.data})
	        else:
	            return Response({'code': 101, 'msg': ser.errors})

删除单条

	class BookDetailView(APIView):
	    def delete(self,request,pk):
	       models.Book.objects.filter(pk=pk).delete()
	       return Response({'code': 100, 'msg': '删除一条成功'})

反序列化的校验

反序列化的数据校验功能类比forms组件

  • 局部钩子
  • 全局钩子

代码实现

from rest_framework import serializers
from rest_framework.exceptions import ValidationError
from .models import Book

class BookSerializer(serializers.ModelSerializer):
    name = serializers.CharField(max_length=18, min_length=2, required=True)
    price = serializers.IntegerField(required=True)
    publish = serializers.CharField(min_length=3)

    class Meta:
        model = Book
        fields = '__all__'

    # 新增一条数据
    def create(self, validated_data):
        # 保存的逻辑
        # validated_data 校验过后的数据 {name,price,publish}
        # 保存到数据库
        book = Book.objects.create(**validated_data)
        # 一定不要忘记返回新增的对象
        return book


    # 修改对象
    def update(self, instance, validated_data):
        # instance 要修改的对象
        # validated_date 校验过后的数据
        instance.name = validated_data.get('name')
        instance.price = validated_data.get('price')
        instance.save()  # orm的单个对象,修改了单个对象的属性,只要调用对象.save就可以修改保存到数据库
        return instance  # 记得把修改的对象返回

    # 局部钩子
    def validate_price(self,price):
        if price < 10 or price > 999:
            raise ValidationError('价格不能高于999或者低于10')
        return price

    # 全局钩子
    def validate(self, attrs):
        # 校验过后的数据,出版社后三位文字与书名后三位不能一样
        if attrs.get('publish')[-3] == attrs.get('name')[-3]:
            raise ValidationError('出版社后三位文字不能与书名后三位一样!')
        return attrs

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1325871.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

大文件传输之传输协议TCP和UDP之间的区别

传输协议是一种规定数据包格式、顺序、重传、确认等细节的约定&#xff0c;确保在不同设备之间正确传送和接收数据。目前常见的协议主要有两种&#xff0c;一是TCP&#xff0c;另一是UDP&#xff0c;它们各自有优势和劣势。下面我们来深入了解。 TCP和UDP的特点和区别&#xff…

力扣题目学习笔记(OC + Swift)17. 电话号码的字母组合

17. 电话号码的字母组合 给定一个仅包含数字 2-9 的字符串&#xff0c;返回所有它能表示的字母组合。答案可以按 任意顺序 返回。 给出数字到字母的映射如下&#xff08;与电话按键相同&#xff09;。注意 1 不对应任何字母。 关键字&#xff1a;所有组合 模式识别&#xff1a…

递归算法:二叉树前序、中序、后序遍历解析与递归思想深度剖析

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏: 《linux深造日志》 《高效算法》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 文章目录 一、二叉树的遍历1.1 链式结构二叉树的创建1.1 二叉树结构图 二、 前序遍历代码演示&#xff1a;2.1 前序遍历递…

李飞飞吴恩达等 2024 年 AI 十大预测!GPU算力短缺,AI 智能体一年内大爆发?

2023 这个大模型爆发的元年即将过去&#xff0c;展望未来&#xff0c;比尔盖茨&#xff0c;李飞飞&#xff0c;吴恩达等人对 2024 年人工智能的发展作出了自己的预测。 2023&#xff0c;可以说是人工智能的春天。 在过去的一年里&#xff0c;ChatGPT 成为家喻户晓的名字&#…

InstructPix2Pix:通过用户指令编辑图像

Brooks T, Holynski A, Efros A A. Instructpix2pix: Learning to follow image editing instructions[C]//Proceedings of the IEEE/CVF Conference on Computer Vision and Pattern Recognition. 2023: 18392-18402. InstructPix2Pix 所做的任务是根据用户指令编辑图像。Inst…

SSM整合实战(Spring、SpringMVC、MyBatis)

五、SSM整合实战 目录 一、SSM整合理解 1. 什么是SSM整合&#xff1f;2. SSM整合核心理解五连问&#xff01; 2.1 SSM整合涉及几个IoC容器&#xff1f;2.2 每个IoC容器盛放哪些组件&#xff1f;2.3 IoC容器之间是什么关系&#xff1f;2.4 需要几个配置文件和对应IoC容器关系&…

Ubuntu系统的基础操作和使用

文章目录 系统安装系统界面文件系统包管理命令行常见问题 Ubuntu是一个基于Debian的Linux发行版&#xff0c;以桌面应用为主。它是自由软件&#xff0c;意味着你可以自由地使用、复制、研究、修改和改进这个软件。下面我们将详细介绍Ubuntu系统的基础操作和使用。 系统安装 U…

Android Studio开发之路(六)(合集)界面优化以及启动图标等

一、导航栏背景、字体修改 导航栏、状态栏等背景颜色的修改一般是在themes.xml文件中修改&#xff0c;android一个activity各个部件参考&#xff1a; colorPrimary,colorPrimaryDark等的意义 添加链接描述 但是问题在于&#xff1a;只在这里修改背景颜色的话&#xff0c;可能…

人工智能的发展之路:时间节点、问题与解决办法的全景解析

导言 人工智能的发展历程充满了里程碑式的事件&#xff0c;从早期的概念到今天的广泛应用&#xff0c;每个时间节点都伴随着独特的挑战和创新。本文将详细描述每个关键时间节点的事件&#xff0c;探讨存在的问题、解决办法&#xff0c;以及不同阶段之间的联系。 1. 195…

[学习笔记]SQL Server中批量查找所有符合Where条件的记录

目标&#xff1a;在SQL Server中查找所有表的UserId 50的记录 创建一个表变量来存储所有包含’UserId’列的表的名称。然后使用一个游标遍历这些表&#xff0c;并对每个表执行一个动态SQL查询 DECLARE TableName nvarchar(256), ColumnName nvarchar(128), SearchStr2 nvarc…

如何在公网环境使用固定域名远程访问内网BUG管理系统协同办公

文章目录 前言1. 本地安装配置BUG管理系统2. 内网穿透2.1 安装cpolar内网穿透2.2 创建隧道映射本地服务3. 测试公网远程访问4. 配置固定二级子域名4.1 保留一个二级子域名5.1 配置二级子域名6. 使用固定二级子域名远程 前言 BUG管理软件,作为软件测试工程师的必备工具之一。在…

【大模型实践】Langchain-Chatchat构建对话模型(二)

本文介绍如何使用Langchain-Chatchat构建论文知识库和文件对话。 关于Langchain-Chatchat&#xff1a; &#x1f916;️ 一种利用 langchain 思想实现的基于本地知识库的问答应用&#xff0c;目标期望建立一套对中文场景与开源模型支持友好、可离线运行的知识库问答解决方案。 …

基于vue-cli快速发布vue npm 包

一、编写组件 1. 初始化项目并运行 vue create vue-digital-countnpm run serve2. 组件封装 新建package文件夹 ​ 因为我们可能会封装多个组件&#xff0c;所以在src下面新建一个package文件夹用来存放所有需要上传的组件。 ​ 当然&#xff0c;如果只有一个组件&#xff…

云原生系列2-CICD持续集成部署-GitLab和Jenkins

1、CICD持续集成部署 传统软件开发流程&#xff1a; 1、项目经理分配模块开发任务给开发人员&#xff08;项目经理-开发&#xff09; 2、每个模块单独开发完毕&#xff08;开发&#xff09;&#xff0c;单元测试&#xff08;测试&#xff09; 3、开发完毕后&#xff0c;集成部…

【人生苦短,我学 Python】(9)分支判断和循环

目录 简述 / 前言1. 顺序结构2. 选择结构2.1 单分支2.2 双分支2.3 多分支 3. 循环结构3.1 for 循环3.1.1 range() 3.2 while 循环3.2.1 break 语句3.2.1 continue 语句 3.3 死循环 4. enumerate 函数5. zip 函数文章传送门 简述 / 前言 前面讲了输入、输出和文件的读写&#x…

【UML】第5篇 UML中的视图和图

目录 一、视图和图 二、图的种类 2.1 结构图 2.2 行为图 图是UML中最重要的概念了&#xff0c;起码我是这么认为。 上篇关于低代码的文章&#xff0c;我也说了&#xff0c;未来也许AI编码&#xff0c;我们更重要的工作&#xff0c;是能够为业务进行建模&#xff0c;拆解&a…

【51单片机系列】C51中的中断系统扩展实验

本文是关于51单片机中断系统的扩展实验。 文章目录 一、 扩展实验一&#xff1a;使用外部中断0控制蜂鸣器&#xff0c;外部中断1控制直流电机二、扩展实验二&#xff1a;修改定时器初值&#xff0c;设定3秒钟的定时时间让LED模块闪烁三、扩展实验三&#xff1a;使用定时器1和数…

KBP310-ASEMI适配高端电源KBP310

编辑&#xff1a;ll KBP310-ASEMI适配高端电源KBP310 型号&#xff1a;KBP310 品牌&#xff1a;ASEMI 封装&#xff1a;KBP-4 最大平均正向电流&#xff1a;3A 最大重复峰值反向电压&#xff1a;1000V 产品引线数量&#xff1a;4 产品内部芯片个数&#xff1a;4 产品内…

哈希算法专栏二《力扣题目练习》

引言 在了解了哈希表的基础理论之后&#xff0c;我们就可以开始进行刷题实战了。下面是我在力扣上找的一些题目&#xff0c;认真刷完并理解下面的题目&#xff0c;相信读者至少可以初步掌握哈希算法的思想了。 LeetCode242有效的字母异位词 242. 有效的字母异位词 已解答 简…

智能化安防与监控:全球发展、挑战与未来趋势

导言 智能化安防与监控系统在全球范围内得到广泛应用&#xff0c;成为社会安全和公共管理的重要工具。本文将深入研究其发展历程、遇到的问题及解决过程、未来的可用范围&#xff0c;以及在各国的应用和未来的研究趋势&#xff0c;以探讨在哪些方面能取胜&#xff0c;并在哪些方…