DRF初识与序列化
一、Django的序列化方法
1、为什么要用序列化组件
做前后端分离的项目,我们前后端数据交互一般都选择JSON,JSON是一个轻量级的数据交互格式。
那么我们给前端数据的时候都要转成json格式,那就需要对我们从数据库拿到的数据进行序列化。
2、表的构建
- CHOICES = ((1, "python"), (2, "linux"), (3, "go"))
- # 书籍表
- class Book(models.Model):
- title = models.CharField(max_length=64)
- category = models.IntegerField(choices=CHOICES) # 书籍分类
- pub_time = models.DateField()
- publisher = models.ForeignKey(to="Publisher")
- authors = models.ManyToManyField(to="Author")
- class Meta:
- verbose_name = '书籍'
- verbose_name_plural = verbose_name
- def __str__(self):
- return self.title
- # 出版社表
- class Publisher(models.Model):
- title = models.CharField(max_length=64)
- class Meta:
- verbose_name = '出版社'
- verbose_name_plural = verbose_name
- def __str__(self):
- return self.title
- # 作者表
- class Author(models.Model):
- name = models.CharField(max_length=32)
- class Meta:
- verbose_name = '作者'
- verbose_name_plural = verbose_name
- def __str__(self):
- return self.name
3、希望构建的字典格式
- book_list = [
- {
- "id": 1,
- "title": "",
- "publisher": { # 外键
- "id": 1
- "title": ""
- },
- "authors": [{}, {}] # 多对多
- },
- {
- "id": 2,
- "title": "",
- "publisher": { # 外键
- "id": 1
- "title": ""
- },
- "authors": [{}, {}] # 多对多
- },
- ]
4、方法一
- 在Django中使用Json模块序列化
- class BooksView(views.View):
- def get(self, request):
- book_queryset = Book.objects.values("id", "title", "pub_time", "publisher")
- book_list = list(book_queryset) # 把queryset类型转换成列表
- # 如果我们需要取外键关联的字段信息 需要循环获取外键 再去数据库查然后拼接成我们想要的
- ret = []
- for book in book_list:
- publisher_obj = Publisher.objects.filter(id=book["publisher"]).first()
- # 修改原字典中的publisher对应的值
- book["publisher"] = {
- "id": publisher_obj.id,
- "title": publisher_obj.title
- }
- # 把新的字典追加到一个空列表中
- ret.append(book)
- ret = json.dumps(ret, ensure_ascii=False, cls=MyJson)
- return HttpResponse(ret)
- # json不能序列化时间字段,重写JSONEncoder里面的default方法解决
- class MyJson(json.JSONEncoder):
- def default(self, field):
- if isinstance(field, datetime.datetime):
- return field.strftime('%Y-%m-%d %H:%M:%S')
- elif isinstance(field, datetime.date):
- return field.strftime('%Y-%m-%d')
- else:
- return json.JSONEncoder.default(self, field)
5、方法二
- 使用JsonResponse,自动帮我们重写了JSONEncoder里面的default方法,解决时间字段的问题
- class BooksView(views.View):
- def get(self, request):
- book_queryset = Book.objects.values("id", "title", "pub_time", "publisher")
- book_list = list(book_queryset) # 把queryset类型转换成列表
- # 如果我们需要取外键关联的字段信息 需要循环获取外键 再去数据库查然后拼接成我们想要的
- ret = []
- for book in book_list:
- publisher_obj = Publisher.objects.filter(id=book["publisher"]).first()
- # 修改原字典中的publisher对应的值
- book["publisher"] = {
- "id": publisher_obj.id,
- "title": publisher_obj.title
- }
- # 把新的字典追加到一个空列表中
- ret.append(book)
- return JsonResponse(ret, safe=False, json_dumps_params={'ensure_ascii': False})
6、方法三
- 使用Django自带的序列化模块
- from django.core import serializers
- # 能够得到我们要的效果 结构有点复杂
- class BooksView(views.View):
- def get(self, request):
- book_queryset = Book.objects.all()
- ret = serializers.serialize("json", book_queryset, ensure_ascii=False)
- return HttpResponse(ret)
二、DRF序列化的介绍
1、介绍
- 下载DRF模块:pip install djangorestframework
- 导入:
- from rest_framework.views import APIView
- from rest_framework.response import Response
- 使用DRF默认的页面,需要在settings的APP注册rest_framework
- INSTALLED_APPS = [
- 'django.contrib.admin',
- 'django.contrib.auth',
- 'django.contrib.contenttypes',
- 'django.contrib.sessions',
- 'django.contrib.messages',
- 'django.contrib.staticfiles',
- 'rest_framework',
- ]
- 首先,我们要用DRF的序列化,就要遵循DRF框架的一些标准,
- -- Django我们CBV继承类是View,现在DRF我们要用APIView
- -- Django中返回的时候我们用HTTPResponse,JsonResponse,render ,DRF我们用Response
2、APIView跟View区别
- -- APIView继承了View
- -- APIView的as_view方法实现了csrf中间件的豁免(用DRF不需要再把settings的csrf中间件注释掉了)
- -- 重新封装了request
- request._request可以拿到旧的request
- request.query_params 旧的request.GET即_request.GET
- request.data 除了GET请求外的所有的数据,_request.POST、_request.FILES等
-- 序列化器对象.data
存放序列化好的数据
校验通过的数据存放到validated_data里,最后也会序列化封装到序列化对象.data里面返回给前端
校验不通过,错误信息存到序列化对象.errors里面
-- DRF有自己的序列化模块
from rest_framework import serializers- -- Response
- 继承了HttpResponse
- 携带HTTP标准状态码
- 做模板的渲染
3、使用方法
- -- 序列化(传数据到前端)
- -- 声明一个序列化器
- class BookSerializer(serializers.Serializer):
- id = serializers.IntegerField(required=False)
- title = serializers.CharField(max_length=32)
- pub_time = serializers.DateField()
- -- 视图里序列化我们的queryset或者某个对象
- # queryset需要声明many=True
- ser_obj = BookSerializer(queryset, many=True)
- # 具体的某个的对象则不需要声明
- ser_obj = BookSerializer(book_obj)
- return Response(ser_obj.data)
- -- 实现流程
- -- 如果指定了many=True
- -- 把queryset当成可迭代对象去循环 得到每个模型对象
- -- 把每个模型对象放入序列号器进行序列化
- -- 进行字段匹配 匹配上的字段进行序列化 匹配不上丢弃
- -- 必须满足序列化的所有字段要求
- -- 反序列化(从前端获取数据)
- -- 获取前端传过来的数据
- -- 用序列化器进行校验
- # 新增一条数据
- ser_obj = BookSerializer(data=request.data)
- # 编辑某条数据
- # instance编辑哪个对象,data前端传过来要跟新的某些字段数据,partial表示允许部分跟新
- ser_obj = BookSerializer(instance=book_obj, data=request.data, partial=True)
- if ser_obj.is_valid():
- ser_obj.save()# 调用create/update方法
- return Response(ser_obj.data)
- else:
- return Response(ser_obj.errors)
- -- 写create方法
- 在create方法里用ORM操作创建新对象
- -- 写update方法
- 在update方法里用ORM操作创编辑对象
4、注意事项
- 1. 外键和多对多字段的序列化需要额外再设置序列化器
- 2. 序列化
- 序列化器的参数是queryset和many
- 3. 反序列化
- 序列化器的参数是data=提交上来的数据
- 4. 序列化器字段类型不统一的情况
- 反序列化要用的一些字段通过一些参数跟序列化区分开
- -- required=False
- -- read_only=True
- -- write_only=True
- 5. 反序列化的验证
- is_valid() --> 校验数据
- post请求中的save() --> 调用序列化器的create方法
put请求中的save() --> 调用序列化器的update方法
三、DRF序列化示例
1、声明序列化器
- # serializers.py文件
- from rest_framework import serializers
- from libsys.models import Book
- CHOICES = ((1, "python"), (2, "linux"), (3, "go"))
- # 继承serializers.Serializer
- class PublisherSerializer(serializers.Serializer):
- id = serializers.IntegerField()
- title = serializers.CharField(max_length=64)
- class AuthorSerializer(serializers.Serializer):
- id = serializers.IntegerField()
- name = serializers.CharField(max_length=32)
- # 字段的声明和models相类似
- class BookSerializer(serializers.Serializer):
- # POST校验的时候required=False声明不需要校验这个字段
- id = serializers.IntegerField(required=False)
- title = serializers.CharField(max_length=64)
- pub_time = serializers.DateField()
- # 选择字段,显示的是数字
- # category = serializers.ChoiceField(choices=CHOICES)
- # 把选择字段显示成字符类型,source参数后面跟的是ORM操作
- # read_only=True表示这个字段只在渲染前端的时候使用
- category = serializers.CharField(source='get_category_display', read_only=True)
- # write_only=True表示这个字段只在POST提交数据,做校验的时候使用
- post_category = serializers.IntegerField(write_only=True)
- # 外键需要设置额外的序列化器对它进行序列化
- publisher = PublisherSerializer(read_only=True)
- publisher_id = serializers.IntegerField(write_only=True)
- # 多对多字段需要设置额外的序列化器对它进行序列化,且声明many=True
- authors = AuthorSerializer(many=True, read_only=True)
- author_list = serializers.ListField(write_only=True)
- def create(self, validated_data): # validated_data是通过校验的数据,最后也会封装到data里面
- #通过ORM操作给Book表增加数据
- book_obj = Book.objects.create(title=validated_data["title"], pub_time=validated_data["pub_time"],
- category=validated_data["post_category"], publisher_id=validated_data["publisher_id"])
- book_obj.authors.add(*validated_data["author_list"])
- return book_obj
- def update(self, instance, validated_data):
- # 通过ORM操作给Book表编辑数据
- # instance就是book_obj
- instance.title = validated_data.get("title", instance.title)
- instance.pub_time = validated_data.get("pub_time", instance.pub_time)
- instance.category = validated_data.get("post_category", instance.category)
- instance.publisher_id = validated_data.get("publisher_id", instance.publisher_id)
- if validated_data.get("author_list", False):
- instance.authors.set(validated_data["author_list"])
- instance.save()
- return instance
2、在视图函数中调用
- from libsys.models import Book, Publisher, Author
- from rest_framework.views import APIView
- from rest_framework.response import Response
- from .serializers import BookSerializer
- # Create your views here.
- # 书籍列表
- class BookView(APIView):
- def get(self, request):
- book_queryset = Book.objects.all()
- # 声明一个序列化器
- # 用序列化器去序列化queryset(queryset有多个对象的时候,需要声明many=True)
- # 把数据提交到序列化器,跟序列化器的字段进行匹配,匹配成功就进行序列化
- ser_obj = BookSerializer(book_queryset, many=True)
- return Response(ser_obj.data)
- # 新增书籍
- def post(self, request):
- # 前端传过来的数据应该也是这样的
- # {"id""title",
- # category: 1
- # publisher: 1
- # authors: [1, 2]
- # }
- # 获取前端传过来的数据
- book_obj = request.data
- # 用序列化器做校验
- ser_obj = BookSerializer(data=book_obj)
- if ser_obj.is_valid():
- # 校验通过,新增书籍
- ser_obj.save() # 这里的save方法会去调用序列化器的create方法
- print(ser_obj.validated_data) # validated_data是通过校验的数据,也会封装到data里面
- return Response(ser_obj.data)
- # 校验不通过返回错误信息
- return Response(ser_obj.errors)
- # 编辑书籍
- class BookEditView(APIView):
- def get(self, request, id):
- book_obj = Book.objects.filter(pk=id).first()
- ser_obj = BookSerializer(book_obj)
- return Response(ser_obj.data)
- def put(self, request, id):
- book_obj = Book.objects.filter(id=id).first()
- ser_obj = BookSerializer(instance=book_obj, data=request.data, partial=True)
- if ser_obj.is_valid():
- ser_obj.save()
- return Response(ser_obj.data)
- return Response(ser_obj.errors)
四、验证
1、 单个字段的验证(局部钩子)
- class BookSerializer(serializers.Serializer):
- id = serializers.IntegerField(required=False)
- title = serializers.CharField(max_length=64)
- 其他字段...
- # 局部钩子方法:validate_字段名,value 是提交过来的这个字段的数据
- def validate_title(self, value):
- # 定义校验规则:标题必须含有python、linux、go
- course_list = ['python', 'linux', 'go']
- for course in course_list:
- if course in value.lower():
- return value
- else:
- raise serializers.ValidationError('输入的书籍名不合法')
2、 多个字段的验证(全局钩子)
- class BookSerializer(serializers.Serializer):
- post_category = serializers.IntegerField(write_only=True)
- publisher_id = serializers.IntegerField(write_only=True)
- 其他字段...
- # 全局钩子方法:validate,attrs 是前端传过来的所有的数据组成的字典
- def validate(self, attrs):
- # 定义校验规则:书籍分类和作者id不能超过3
- if attrs['post_category'] > 3 or attrs['publisher_id'] > 3:
- raise serializers.ValidationError('输入的图书分类或作者不存在')
- return attrs
3、 自定义校验规则
- def my_validate(value):
- if '周星星' in value:
- raise serializers.ValidationError('输入的书籍太帅,不合法')
- return value
- class BookSerializer(serializers.Serializer):
- id = serializers.IntegerField(required=False)
- # validators声明校验的规则
- title = serializers.CharField(max_length=64, validators=[my_validate,])
4、校验的权重
自定义校验 > 局部钩子 > 全局钩子
五、ModelSerializer
1、介绍
跟Django的Form组件类似,我们使用DRF进行前后端数据交互,有很多需要序列化和反序列化的字段都跟models模型相关,
那么,DRF也给我们提供了跟模型紧密相关的序列化器:ModelSerializer
-- 继承serializers.ModelSerializer
-- 它和Form有点类似
-- 它会根据模型自动生成一组字段
-- 它默认实现了.update()以及.create()方法
2、定义ModelSerializer序列化器
- class BookSerializer(serializers.ModelSerializer):
- class Meta:
- model = Book
- fields = "__all__"
- # fields = ["id", "title", "pub_time"]
- # exclude = ["user"]
- # 分别是所有字段 包含某些字段 排除某些字段
3、外键关系的字段
- 当序列化类META中定义了depth时,这个序列化类中引用字段(外键)则自动变为只读
- class BookSerializer(serializers.ModelSerializer):
- class Meta:
- model = Book
- fields = "__all__"
- # fields = ["id", "title", "pub_time"]
- # exclude = ["user"]
- # 分别是所有字段 包含某些字段 排除某些字段
- # depth 代表找嵌套关系的第几层
- depth = 1
4、META中的其他参数
- class BookSerializer(serializers.ModelSerializer):
- class Meta:
- model = Book
- fields = "__all__"
- # fields = ["id", "title", "pub_time"]
- # exclude = ["user"]
- # 分别是所有字段 包含某些字段 排除某些字段
- # depth 代表找嵌套关系的第几层
- depth = 1
- # 只读字段
- read_only_fields = ["id"]
- # 给某些字段设置额外参数
- extra_kwargs = {"title": {"validators": [my_validate,]}}
5、SerializerMethodField方法字段
- 外键关联的对象有很多字段我们是用不到的,如果都传给前端会有数据冗余,
- 就需要我们自己去定制序列化外键对象的哪些字段。
- 使用了方法字段,它会自动去找对应的钩子函数(get_字段名),这个方法字段展示的值就是钩子函数的返回值
- 钩子函数的参数obj:就是序列化的每个模型对象 book_obj
- class BookSerializer(serializers.ModelSerializer):
- # 方法字段
- # SerializerMethodField 会去找钩子方法 钩子方法的返回值给这个字段
- # 钩子函数:get_字段名称
- category_text = serializers.SerializerMethodField(read_only=True)
- publisher_info = serializers.SerializerMethodField(read_only=True)
- author_info = serializers.SerializerMethodField(read_only=True)
- def get_category_text(self, obj):
- # obj就是序列化的每个模型对象 book_obj
- return obj.get_category_display()
- def get_publisher_info(self, obj):
- return {"id": obj.publisher_id, "title": obj.publisher.title}
- def get_author_info(self, obj):
- return [{"id": author.id, "name": author.name} for author in obj.authors.all()]
- class Meta:
- model = Book
- fields = "__all__"
6、完整的ModelSerializer思路
- 因为depth会让我们外键变成只读,所以一般来说我们不用它,
- 因为如果是前端发数据过来(post,put等),我们对外键、多对多等字段进行验证是验证其在数据库的中真实值,
- 因此,把ModelSerializer序列化器fields代表的字段应该用于反序列化,而对序列化到前端的外键、多对多等字段的数据,
- 我们可以使用SerializerMethodField对其进行处理后展示到前端。
- class BookSerializer(serializers.ModelSerializer):
- # 方法字段
- category_text = serializers.SerializerMethodField(read_only=True)
- publisher_info = serializers.SerializerMethodField(read_only=True)
- author_info = serializers.SerializerMethodField(read_only=True)
- # 方法字段的值,取决于它对应的方法字段的钩子函数的返回值
- def get_category_text(self, obj):
- # obj就是序列化的每个模型对象 book_obj
- return obj.get_category_display()
- def get_publisher_info(self, obj):
- return {"id": obj.publisher_id, "title": obj.publisher.title}
- def get_author_info(self, obj):
- return [{"id": author.id, "name": author.name} for author in obj.authors.all()]
- class Meta:
- model = Book
- fields = "__all__"
- # fields = ["id", "title", "pub_time"]
- # exclude = ["user"]
- # 分别是所有字段 包含某些字段 排除某些字段
- # depth 代表找嵌套关系的第几层
- # depth = 1
- extra_kwargs = {
- "category": {'write_only': True},
- "publisher": {'write_only': True},
- "authors": {'write_only': True},
- }
DRF初识与序列化的更多相关文章
- DRF中的序列化器
DRF中的序列化器详细应用 视图的功能:说白了就是接收前端请求,进行数据处理 (这里的处理包括:如果前端是GET请求,则构造查询集,将结果返回,这个过程为序列化:如果前端是POST请求,假如要对数 ...
- 【DRF框架】序列化组件
DRF框架的序列化组件 在前后端分离的应用模式中,后端仅返回前端所需的数据,返回的数据类似是JSON,因此需要使用序列化组件进行序列化再将数据返回 使用JsonResponse做序列化 # 使用Js ...
- 经历了源码的痛苦,掌握DRF的核心序列化器
目录 DRF的核心--序列化器 序列化器 什么是序列化和反序列化? 序列化 序列化demo 字段类型 字段参数 序列化自定制返回字段 方法一:在序列化类(serializers.py)中写 方法二:在 ...
- DRF分阶段序列化细化实例
DRF是分两阶段进行的. 如果是API接收数据,则是先进行Parser,将外界接收的bytes数据分析成python数据类型,其间涉及encoding操作,再进行序列化,将python数据类型保存进数 ...
- drf:筛选,序列化
1.基础 restful规范: - url:一般用名词 http://www.baidu.com/article (面向资源编程) - 根据请求方式的不同做不同操作:get,post,put,dele ...
- DRF项目之序列化器和视图重写方法的区别
我们,都知道,DRF框架是一款高度封装的框架. 我们可以通过重写一些方法来实现自定义的功能. 今天,就来说说在视图中重写和序列化器中重写方法的区别. 在视图中重写方法: 接收请求,处理数据(业务逻辑) ...
- DRF框架之序列化器初体验
首先,我们需要明白序列化和反序列化的过程指的是什么. 序列化操作:将模型数据 ---> 字典数据 --->JSON数据(响应JSON数据的操作) 反序列化操作:将JSON数据 ---> ...
- 二.drf之使用序列化编写视图
总结:两功能序列化: a.拿到queryset --->idc = Idc.objects.all() b.将queryset给序列化成类---->serializer = IdcSeri ...
- day71:drf:API接口&Restful API规范&Django Rest Framework&drf中的序列化和反序列化功能
目录 1.web应用模式 2.API接口 3.Restful API规范 4.序列化 5.Django Rest Framework 1.drf的简单介绍 2.drf的特点 3.如何安装drf 4.d ...
随机推荐
- vue2.x 在引用插件的时候,npm run dev跑正常 ,npm run build 报错vue-cli Unexpected token: punc (() [
这是因为,引用的插件在node_modules里,并不在vue-cli的es6编译范围内,所以语法报错,修改方法:
- Html和Css学习笔记-html进阶-html5属性
我的邮箱地址:zytrenren@163.com欢迎大家交流学习纠错! 此篇博客是我的复习笔记,html和css学的时间太久了,忘得差不多了,最近要使用一下,所以重新打开html的书略读,后记录了标签 ...
- ArcGIS API for JavaScript 4.2学习笔记[20] 使用缓冲区结合Query对象进行地震点查询【重温异步操作思想】
这个例子相当复杂.我先简单说说这个例子是干啥的. 在UI上,提供了一个下拉框.两个滑动杆,以确定三个参数,使用这三个参数进行空间查询.这个例子就颇带空间查询的意思了. 第一个参数是油井类型,第二个参数 ...
- 虚拟机下centos7.x简易命令大全与试玩体验
OS: liunxversion: centos7.xdate: 2019-01-18 1. cd / : 进入服务器根目录2. cd . ...
- 使用 Nexus Repository Manager 搭建 npm 私服
目录 环境 下载与安装 添加npm仓库 配置与验证npm仓库 发布自己的包 Nexus开启启动 脚注 环境 windows10(1803) Nexus Repository Manager OSS 3 ...
- Windows系统下搭建Git本地代码库
近由于工作需要,要把工作代码做一下版本管理.工作代码也不方便放到github上,也不想付费建私密库,公司也没几个人,所以就想着搭建一个本地Git版本库,来做版本管理.搭建过程如下. 系统环境:Dell ...
- c#核心基础-委托
委托是一个类型.C#中的委托是面向对象的,并且它是类型安全的 当创建委托实例的时候,创建的实例会包含一个调用列表,在调用列表中可以包含多个方法.每个方法称作一个调用实体.调用实体可以是静态方法,也可以 ...
- c/c++ 多线程 等待一次性事件 future概念
多线程 等待一次性事件 future概念 背景:有时候,一个线程只等待另一个线程一次,而且需要它等待的线程的返回值. 案例:滴滴叫车时,点完了叫车按钮后,叫车的后台线程就启动了,去通知周围的出租车.这 ...
- 【Eclipse】springMVC介绍与配置
SpringMCV介绍: Spring MVC是一种基于Java的实现了MVC设计模式的请求驱动类型的轻量级Web框架,即使用了MVC架构模式的思想,将web层进行职责解耦,基于请求驱动的,也就是使用 ...
- Redis可视化工具 Redis Desktop Manager
1.前言 从接触Redis也有两年,平时就使用它来做缓存层,它给我的印象就是很强大,内置的数据结构很齐全,加上Redis5.0的到来,新增了很多特色功能.而Redis5.0最大的新特性就是多出了一个数 ...