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 date_sunrise() 函数

    ------------恢复内容开始------------ 实例 返回葡萄牙里斯本今天的日出时间: <?php// Lisbon, Portugal:// Latitude: 38.4 Nor ...

  2. PHP quoted_printable_encode() 函数

    定义和用法 quoted_printable_encode() 函数把 8 位字符串转换为 quoted-printable 字符串. 提示:经过 quoted-printable 编码后的数据与通过 ...

  3. Vue无限滚动加载数据

    Web项目经常会用到下拉滚动加载数据的功能,今天就来种草Vue-infinite-loading 这个插件,讲解一下使用方法! 第一步:安装 npm install vue-infinite-load ...

  4. zabbix监控服务部署

    目录 zabbix监控服务部署 1. zabbix介绍 1.1 zabbix的组件 1.2 zabbix的进程 1.3 zabbix常用术语 2. zabbix工作原理 3. zabbix监控架构 4 ...

  5. Archlinux 最新安装方法 (2020.07.01-x86_64)之虚拟机 BIOS 安装

    话不多说,直接上干货 准备 去Arch 官网,选择一个合适的国内镜像站下载 Arch 安装包 ISO,地址如下: https://www.archlinux.org/download/ 一.创建虚拟机 ...

  6. Junit4 测试代码

    Junit4 测试代码 import org.junit.Test; import org.junit.runner.RunWith; @RunWith(SpringJUnit4ClassRunner ...

  7. Python的10个神奇的技巧

    尽管从表面上看,Python似乎是任何人都可以学习的一种简单语言,但确实如此,许多人可能惊讶地知道一个人可以熟练掌握该语言. Python是其中的一门很容易学习的东西,但可能很难掌握. 在Python ...

  8. JS 本地存储笔记

    本地存储     1.数据存储在用户浏览器中的     2.设置.读取方便.甚至刷新都不会丢失数据     3.容量比较大,sessionStorange约5M,localstorage约20M    ...

  9. LDAP 使用记录

    LDAP 命令记录 工作中用到了 LDAP,做一个简单记录. 概念性的东西不做阐述,只是记录常用命令,以便将来回顾. 想多做了解可以参考这个系列文章: https://blog.csdn.net/li ...

  10. 解决Android v4、v7包导入标红问题import android.support.v4.app.ActivityCompat;import android.support.v7.app

    如果有如下错误:import android.support.v4.app.ActivityCompat;import android.support.v7.app.AppCompatActivity ...