Serializers组件

使用背景

因为每个语言都有自己的数据类型,不同语言要想数据传输,就必须指定一种各种语言通用的数据类型,如json,xml等等

序列化器允许把像查询集和模型实例这样的复杂数据转换为可以轻松渲染成JSONXML或其他内容类型的原生Python类型

示例(我将用3个例子引出今天的主题drf中的serializers)

from django.db import models

__all__ = ['Book','Publisher','Author']
CHOICES = [(1,'python'),(2,'go')]
class Book(models.Model): title = models.CharField('书名',max_length=12)
category = models.IntegerField('类别',choices=CHOICES)
pub_time = models.DateField('出版时间')
publisher = models.ForeignKey('Publisher',verbose_name='出版社')
authors = models.ManyToManyField('Author',verbose_name='作者') def __str__(self):
return self.title class Meta:
verbose_name = '书籍'
verbose_name_plural = verbose_name
class Author(models.Model):
name = models.CharField('姓名',max_length=12) def __str__(self):
return self.name class Meta:
verbose_name = '作者'
verbose_name_plural = verbose_name class Publisher(models.Model):
title = models.CharField('出版社',max_length=12)
def __str__(self):
return self.title
class Meta:
verbose_name = '出版社'
verbose_name_plural = verbose_name

全文我都会用上面的model来做讲解

第一版(使用json.dumps序列化python数据类型,通过HttpResponse返回)

class BookView(View):
def get(self,request,*args,**kwargs):
book_queryset = models.Book.objects.values('id','title','category')
str_book = json.dumps(list(book_queryset),ensure_ascii=False)
return HttpResponse(str_book)
// 因为python内置的json不支持序列化时间类型的数据,所以使用json.dumps存在局限性

第二版(使用django自带的JsonResponse返回所需的数据)

class BookView(View):
def get(self,request,*args,**kwargs):
book_queryset =models.Book.objects.values('id','title','pub_time','category','publisher')
book_list = list(book_queryset)
return JsonResponse(book_list,safe=False)
打印结果
[{"id": 1, "title": "金品梅", "pub_time": "2020-06-04", "category": 2, "publisher": 1}, {"id": 2, "title": "曾经爱过", "pub_time": "2020-06-04", "category": 2, "publisher": 2}]
// 通过JsonResponse支持序列化时间类型的数据,但通过观察数据可发现,category和publisher并不是我们想要的数据格式,我们更希望传给前端的数据是具体的,而不是id,所以使用JsonResponse序列化的数据也不尽人意

第三版(使用django中serializers序列化数据)

class BookView(View):
def get(self,request,*args,**kwargs):
book_queryset = models.Book.objects.all()
ret = serializers.serialize('json',book_queryset,ensure_ascii=False)
return HttpResponse(ret)
//打印出的数据
[{"model": "app.book", "pk": 1, "fields": {"title": "金品梅", "category": 2, "pub_time": "2020-06-04", "publisher": 1, "authors": [1, 2]}}, {"model": "app.book", "pk": 2, "fields": {"title": "曾经爱过", "category": 2, "pub_time": "2020-06-04", "publisher": 2, "authors": [2, 3]}}]
//通过打印后的结果可以看出,同样存在局限性,有很多我们不想要的数据,所以django自带的serializers也不是很给力

drf中serializers

通过前几版的序列化组件可以看出,django中自带的序列化不能很好的满足我们的需求,所以就引出restframework中的serializers

首先我们要遵循drf中的标准,使用CBV模式,且继承APIView,响应时返回Response

序列化

app/serializer
// 首先创建一个py文件
from rest_framework import serializers
class PublisherSerializers(serializers.Serializer):
id = serializers.IntegerField()
title = serializers.CharField(max_length=12) class AuthorSerializers(serializers.Serializer):
id = serializers.IntegerField()
name = serializers.CharField(max_length=12) class BookSerializers(serializers.Serializer):
id = serializers.IntegerField(required=False)
title = serializers.CharField(max_length=12)
category = serializers.CharField(source='get_category_display') //choices要想返回可读的数据类型,则需要制定source,里面的内容填orm的操作
pub_time = serializers.DateField()
publisher = PublisherSerializers()
authors = AuthorSerializers(many=True) //对于外键的操作,则需要重新定义一个序列化类,里面的字段则是需要序列化的字段
对于多对多外键,则需要添加many=True
//上述定义的字段,字段名要与model中相同,只能少不能多 app/views
from .serializer import BookSerializers
class BookView(APIView):
def get(self,request,*args,**kwargs):
book_queryset = models.Book.objects.all()
book_sel = BookSerializers(book_queryset,many=True)
return Response(book_sel.data)
//返回的结果
[
{
"id": 1,
"title": "金品梅",
"category": "go",
"pub_time": "2020-06-04",
"publisher": {
"id": 1,
"title": "菊花出版社"
},
"authors": [
{
"id": 1,
"name": "will"
},
{
"id": 2,
"name": "weson"
}
]
},
{
"id": 2,
"title": "曾经爱过",
"category": "go",
"pub_time": "2020-06-04",
"publisher": {
"id": 2,
"title": "奥利给出版社"
},
"authors": [
{
"id": 2,
"name": "weson"
},
{
"id": 3,
"name": "scott"
}
]
}
]
//很显然,是我们需要的数据格式

反序列化

刚刚上面讲解了后端发数据给前端,自然就有前端发数据给后端,前端发送数据的同时,我们要进行一些校验然后

保存到数据库,当然这些校验工作,drf的serializers也给我们提供了方法,

反序列化用的字段要和序列化区分开

serializer提供了.is_valid() 和.save()方法~~

//前端传给我们的数据不用跟后端一样,一般前端传来的数据都是id之类的,所以我们要定义不同的字段来区分正反
app/serializer
class PublisherSerializers(serializers.Serializer):
id = serializers.IntegerField()
title = serializers.CharField(max_length=12) class AuthorSerializers(serializers.Serializer):
id = serializers.IntegerField()
name = serializers.CharField(max_length=12) class BookSerializers(serializers.Serializer):
id = serializers.IntegerField(required=False)
title = serializers.CharField(max_length=12)
category = serializers.CharField(source='get_category_display')
pub_time = serializers.DateField()
post_publisher = serializers.IntegerField(write_only=True)
post_authors = serializers.ListField(write_only=True)
publisher = PublisherSerializers(read_only=True)
authors = AuthorSerializers(many=True,read_only=True)
//值得注意的是,required=False表示该字段不是必须的,有则序列,无则放过,write_only表示只有在前端传过来时才校验,read_only表示只有在后端发送数据才校验 app/views
def post(self,request):
book_sel = BookSerializers(data=request.data) //request.data封装了wsgi原始request.POST的数据流,包含文件和json
if book_sel.is_valid(): //类似于form表达,用于校验传送过来的数据是否合法
book_sel.save() //保存,切记该方法的调用一定要在serializer中重写create方法,且返回实例对象
return Response(book_sel.data) //返回新增的数据
else:
return Response(book_sel.errors) //返回错误

当前端要向后端更新数据时,我们要对部分数据进行校验

局部数据更新可用patch,全部更新可用put(当然这只是规范而已,你也可以用put更新局部数据,并不是标准)

我这里就用patch做局部数据更新演示

app/views
def patch(self,request):
id = request.data['id']
book_obj = models.Book.objects.filter(pk=id).first()
book_sel = BookSerializers(book_obj,data=request.data,partial=True) //这里因为是局部更新,要传入更改的book对象,data传入需要更改的数据,切记partial一定要为True,支持局部更新
if book_sel.is_valid():
book_sel.save()
return Response(book_sel.data)
else:
return Response(book_sel.errors) app/serializer
def update(self, instance, validated_data):
instance.title = validated_data.get('title',instance.title)
instance.category = validated_data.get('category',instance.category)
instance.pub_time = validated_data.get('pub_time',instance.pub_time)
instance.publisher = validated_data.get('post_publisher',instance.publisher)
if validated_data.get('post_authors'):
instance.authors.set(validated_data.get('post_authors'))
instance.save()
return instance //有这更新,无则使用原来的值

其他校验手段

除了自定字段时添加的校验字段,drf还为我们提供了局部钩子来进行数据清洗

单个字段

class BookSerializer(serializers.Serializer):
id = serializers.IntegerField(read_only=True)
title = serializers.CharField(max_length=32)
# 省略了一些字段 跟上面代码里一样的
# 。。。。。
def validate_title(self, value):
if "python" not in value.lower():
raise serializers.ValidationError("标题必须含有Python")
return value

多个字段

 新增了一个上架时间字段
# 省略一些字段。。都是在原基础代码上增加的
# 。。。。。。 # 对多个字段进行验证 要求上架日期不能早于出版日期 上架日期要大
def validate(self, attrs):
if attrs["pub_time"] > attrs["date_added"]:
raise serializers.ValidationError("上架日期不能早于出版日期")
return attrs

内置校验器和自定义校验器

//自定义校验器
def my_validate(value):
if "敏感词汇" in value.lower:
raise serializers.ValidationError("包含敏感词汇,请重新提交")
return value class BookSerializer(serializers.Serializer):
id = serializers.IntegerField(read_only=True)
title = serializers.CharField(max_length=32, validators=[my_validate])
# 。。。。。。
def validate_title(self, value): //内置的局部钩子
if "python" not in value.lower():
raise serializers.ValidationError("标题必须含有Python")
return value
注意:当对一个字段定义内外校验器,则自定义校验器优先级大于局部钩子的

ModelSerializer

通过前面的Serializers案例可以看出,的确满足了我们的需求,但是过于麻烦,且序列化的字段本身就属于model的字段,所以就有了ModelSerializer,方便我们进行数据序列化

定义一个ModelSerializer

class BookSerializer(serializers.ModelSerializer):
class Meta:
model = models.Book //你所需要序列化的model
fields = "__all__" //序列化的字段,__all__表示全部,如果想自定义的话 则用列表表示
# fields = ["id", "title", "pub_time"]
# exclude = ["user"] //fields和exclude和链式关系,并不是并行关系
# 分别是所有字段 包含某些字段 排除某些字段
# 如果只单纯用model和fields,choices和外键关系,默认用引用字段,即数字类型
depth = 1 //该字段专门解析外键字段,depth 代表找嵌套关系的第几层,默认会取出当前层数的所有字段

自定义字段

我们可以声明一些字段来覆盖默认字段,来进行自定制~

比如我们的选择字段,默认显示的是选择的key,我们要给用户展示的是value。

class BookSerializer(serializers.ModelSerializer):
category = serializers.CharField(source="get_category_display", read_only=True) //重写model中category字段 class Meta:
model = Book
fields = "__all__"
# fields = ["id", "title", "pub_time"]
# exclude = ["user"]
# 分别是所有字段 包含某些字段 排除某些字段
depth = 1
通过这种方式显然不可取,我们要反序列化就显得很困难了,另外depth默认只可读,所以我们要重新规划下字段

SerializerMethodField

外键关联的对象有很多字段我们是用不到的都传给前端会有数据冗余就需要我们自己去定制序列化外键对象的哪些字段~~

class BookSerializers(serializers.ModelSerializer):
category_display = serializers.SerializerMethodField(read_only=True) //该字段用于自定义属性类别
publisher_display = serializers.SerializerMethodField(read_only=True)//因为默认只显示主键,所以设置read_only=True
authors_display = serializers.SerializerMethodField(read_only=True) def get_authors_display(self,obj): //格式get_加字段名,obj为当前model实例
return [{'id':author.pk,'name':author.name} for author in obj.authors.all()] //返回需要的数据格式 def get_publisher_display(self,obj):
return {'id':obj.publisher_id,'title':obj.publisher.title} def get_category_display(self,obj):
return obj.get_category_display() class Meta:
model = models.Book
fields = '__all__'
read_only_fields = [xxx] //只序列化的字段
extra_kwargs = {'category':{'write_only':True},'publisher':{'write_only':True},'authors':{'write_only':True}} //extra_kwargs类似于字段中添加的属性

参考链接

https://www.cnblogs.com/GGGG-XXXX/articles/9568816.html Serializers 序列化组件

Serializers组件详解的更多相关文章

  1. Android中Intent组件详解

    Intent是不同组件之间相互通讯的纽带,封装了不同组件之间通讯的条件.Intent本身是定义为一个类别(Class),一个Intent对象表达一个目的(Goal)或期望(Expectation),叙 ...

  2. Android笔记——四大组件详解与总结

     android四大组件分别为activity.service.content provider.broadcast receiver. ------------------------------- ...

  3. vue.js基础知识篇(6):组件详解

    第11章:组件详解 组件是Vue.js最推崇也最强大的功能之一,核心目标是可重用性. 我们把组件代码按照template.style.script的拆分方式,放置到对应的.vue文件中. 1.注册 V ...

  4. Echars 6大公共组件详解

    Echars 六大组件详解 : title  tooltip toolbox legend  dataZoom visualMap 一.title标题详解 myTitleStyle = { color ...

  5. Angular6 学习笔记——组件详解之组件通讯

    angular6.x系列的学习笔记记录,仍在不断完善中,学习地址: https://www.angular.cn/guide/template-syntax http://www.ngfans.net ...

  6. Angular6 学习笔记——组件详解之模板语法

    angular6.x系列的学习笔记记录,仍在不断完善中,学习地址: https://www.angular.cn/guide/template-syntax http://www.ngfans.net ...

  7. admin组件详解

    admin组件详解 先根据admin组件启动流程复习下django项目启动至请求过来发生的事 1将admin组件注册进app 2django项目启动 3在运行到定制的admin时执行其下面的apps文 ...

  8. OpenStack各组件详解和通信流程

    一.openstack由来 openstack最早由美国国家航空航天局NASA研发的Nova和Rackspace研发的swift组成.后来以apache许可证授权,旨在为公共及私有云平台建设.open ...

  9. Tomcat系列之服务器的安装与配置以及各组件详解

    Tomcat系列之服务器的安装与配置以及各组件详解 大纲 一.前言 二.安装与配置Tomcat 三.Tomcat 目录的结构 四.Tomcat 配置文件 注,本文的测试的操作系统为CentOS 6.4 ...

随机推荐

  1. PHP rename() 函数

    定义和用法 rename() 函数重命名文件或目录. 如果成功,该函数返回 TRUE.如果失败,则返回 FALSE. 语法 rename(oldname,newname,context) 参数 描述 ...

  2. 解决痛苦的方法/cy

    这篇文章 源于我所有痛苦的回忆. 由于痛苦太多了 体会完了 所以 觉得这些都不算是什么大问题了 所以 这里 是解决痛苦的方法. 真的很痛苦的话 可以这样 对着全班人喊你们 都没我强 因为 你们都没有我 ...

  3. Scala---初探

    scala语言量大特性:面向对象+函数式编程 Scala的类型 val指的是引用不可变,而不是值. 值类型 Byte Char Short Int Long Float Double 引用类型 Str ...

  4. Jenkins总结1-部署jenkins

    1. 介绍 jenkins是一个广泛用于持续构建的可视化web工具,持续构建说得更直白点,就是各种项目的"自动化"编译.打包.分发部署.jenkins可以很好的支持各种语言(比如: ...

  5. Mysql存储结构

    索引是一种加快查询速度的数据结构,常用索引结构有hash.B-Tree和B+Tree.本节通过分析三者的数据结构来说明为啥Mysql选择用B+Tree数据结构. 数据结构 Hash hash是基于哈希 ...

  6. linux下的node版本管理利器:nvm

    nvm是一款node版本管理工具,简单来说,如果你想在一个环境下安装多个node版本,并向自由地切换相关版本,那你就需要使用nvm进行版本管理,有点类似pyenv,也是一款python版本管理工具. ...

  7. NuGet Package Explorer 中文版

    Id:NuGet Package Explorer 中文版 Description:基于原版 5.7.170 的绿色中文版,无任何‘添加剂’ Version:5.7.170 Download:Gith ...

  8. 极简 Node.js 入门 - 1.3 调试

    极简 Node.js 入门系列教程:https://www.yuque.com/sunluyong/node 本文更佳阅读体验:https://www.yuque.com/sunluyong/node ...

  9. IDEA的基本使用技巧

    博主在大学里学习的专业是计算机科学与技术,在大三的时候才开始接触 “加瓦”,学习加瓦首先就需要一个运行环境,因为受到了老师们的影响,我第一个编辑JAVA的软件环境便是Eclipse,在学校里学习和使用 ...

  10. ZooKeeper Watcher 机制

    前言 在 ZooKeeper 中,客户端可以向服务端注册一个监听器,监听某个节点或者其子节点列表,当监听对象发生变化时,服务端就会向指定的客户端发送通知,这是 ZooKeeper 中的 Watcher ...