Serializers组件详解
Serializers组件
使用背景
因为每个语言都有自己的数据类型,不同语言要想数据传输,就必须指定一种各种语言通用的数据类型,如json,xml等等
序列化器允许把像查询集和模型实例这样的复杂数据转换为可以轻松渲染成JSON
,XML
或其他内容类型的原生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组件详解的更多相关文章
- Android中Intent组件详解
Intent是不同组件之间相互通讯的纽带,封装了不同组件之间通讯的条件.Intent本身是定义为一个类别(Class),一个Intent对象表达一个目的(Goal)或期望(Expectation),叙 ...
- Android笔记——四大组件详解与总结
android四大组件分别为activity.service.content provider.broadcast receiver. ------------------------------- ...
- vue.js基础知识篇(6):组件详解
第11章:组件详解 组件是Vue.js最推崇也最强大的功能之一,核心目标是可重用性. 我们把组件代码按照template.style.script的拆分方式,放置到对应的.vue文件中. 1.注册 V ...
- Echars 6大公共组件详解
Echars 六大组件详解 : title tooltip toolbox legend dataZoom visualMap 一.title标题详解 myTitleStyle = { color ...
- Angular6 学习笔记——组件详解之组件通讯
angular6.x系列的学习笔记记录,仍在不断完善中,学习地址: https://www.angular.cn/guide/template-syntax http://www.ngfans.net ...
- Angular6 学习笔记——组件详解之模板语法
angular6.x系列的学习笔记记录,仍在不断完善中,学习地址: https://www.angular.cn/guide/template-syntax http://www.ngfans.net ...
- admin组件详解
admin组件详解 先根据admin组件启动流程复习下django项目启动至请求过来发生的事 1将admin组件注册进app 2django项目启动 3在运行到定制的admin时执行其下面的apps文 ...
- OpenStack各组件详解和通信流程
一.openstack由来 openstack最早由美国国家航空航天局NASA研发的Nova和Rackspace研发的swift组成.后来以apache许可证授权,旨在为公共及私有云平台建设.open ...
- Tomcat系列之服务器的安装与配置以及各组件详解
Tomcat系列之服务器的安装与配置以及各组件详解 大纲 一.前言 二.安装与配置Tomcat 三.Tomcat 目录的结构 四.Tomcat 配置文件 注,本文的测试的操作系统为CentOS 6.4 ...
随机推荐
- PHP fileperms() 函数
定义和用法 fileperms() 函数返回文件或目录的权限. 如果成功,该函数以数字形式返回权限.如果失败,则返回 FALSE. 语法 fileperms(filename) 参数 描述 filen ...
- PHP ftp_systype() 函数
定义和用法 ftp_systype() 函数返回 FTP 服务器的系统类型标识符. 如果成功,该函数返回系统类型.如果失败,则返回 FALSE. 语法 ftp_systype(ftp_connecti ...
- luogu P5892 [IOI2014]holiday 假期 决策单调性优化dp 主席树
LINK:holiday 考虑第一个subtask. 容易想到n^2暴力枚举之后再暴力计算答案. 第二个subtask 暴力枚举终点可以利用主席树快速统计答案. 第三个subtask 暴力枚举两端利用 ...
- linux的用户扩充权限管理acl和用户使用系统资源的限制
用户扩充权限管理 acl 1.扩充权限的方式 文件扩充权限 ACL 磁盘配额 2.文件扩充权限 1.安全位 安全位 ---set位 SUID SGID set仅可以加给 u.g, 如: ...
- mapstruct解放Java对象转换
摘要 当前web后端开发,都是使用多层工程结构,需要在VO,BO,DTO,DO等各种数据结构中相互转换.这些转换代码都是些比较简单的字段映射,类型转换,重复性工作比较高,可以使用一些工具解放我们的双手 ...
- 【JZOJ4725】质数序列 题解(数学)
题目大意:质数序列是指这个序列中任意两个数的和均为质数.先给出一个序列${a_{n}}$,从中取出元素构成最长质数序列,问其长度并输出序列.若长度相同则求和最大的序列.保证答案唯一. -------- ...
- jersey简单总结与demo
参考链接:https://www.iteye.com/blog/dyygusi-2148029?from=singlemessage&isappinstalled=0 测试代码: https: ...
- tree命令编译使用
有天在linux中使用tree命令时候显示--未找到命令 记下解决过程: wget ftp://mama.indstate.edu/linux/tree/tree-1.6.0.tgz tar xzv ...
- .NetCore 配合 Gitlab CI&CD 实践 - 单体项目
前言 上一篇博文 .NetCore 配合 Gitlab CI&CD 实践 - 开篇,主要简单的介绍了一下 GitLab CI 的持续集成以及持续部署,这篇将通过 GitLab CI 发布一个 ...
- Java查表法实现十进制转化成其它进制
首先了解十进制转化成二级制的原理 156的二进制为: 156 % 2 = 78 …… 0 83 % 2 = 39 …… 0 39 % 2 = 19 …… 1 19 % 2 = 9 …… 1 9 % 2 ...