定义一个 schema

import datetime as dtclass User:def __init__(self, name, email):self.name = nameself.email = emailself.created_at = dt.datetime.now()def __repr__(self):return "<User(name={self.name!r})>".format(self=self)

首先我们定义了一个 User 来代替数据库中的模型,然后根据这个模型来定义对应的序列化分序列化规则:

常用方式

from marshmallow import Schema, fieldsclass UserSchema(Schema):name = fields.Str()email = fields.Email()created_at = fields.DateTime()

通过字典来进行定义

from marshmallow import Schema, fieldsUserSchema = Schema.from_dict({"name": fields.Str(), "email": fields.Email(), "created_at": fields.DateTime()}
)

序列化

通过 Loading 来反序列化

默认情况下,load 方法会将传入字典当中的 value 转为为指定的数据类型:

user_data = {"created_at": "2014-08-11T05:26:03.869245","email": "ken@yahoo.com","name": "Ken",
}
schema = UserSchema()
result = schema.load(user_data)
pprint(result)
# {'name': 'Ken',
#  'email': 'ken@yahoo.com',
#  'created_at': datetime.datetime(2014, 8, 11, 5, 26, 3, 869245)}

从上面我们可以看出,时间的字符串被转化为了 datatime.datetime

通过 validate 参数进行限制

当前端传入内容满足对应的数据类型时,如果我们还希望加入一些内容的限制时,此时我们就可以通过 **validate **参数来进行更多的要求。

from marshmallow import Schema, fields, validate, ValidationErrorclass UserSchema(Schema):name = fields.Str(validate=validate.Length(min=1))permission = fields.Str(validate=validate.OneOf(["read","write","admin"]))age = fields.Int(validate=validate.Range(min=18, max=40))in_data = {"name": "", "permission": "invalid", "age": 71}
try:UserSchema().load(in_data)
except ValidationError as err:pprint(err.messages)# {'age': ['Must be greater than or equal to 18 and less than or equal to 40.'],#  'name': ['Shorter than minimum length 1.'],#  'permission': ['Must be one of: read, write, admin.']}

有关自定义异常条件的书写方式,详见下文:自定义异常

注意: validate 的限制只会在反序列化的时候才会触发。而序列化的内容默认都是有效的。

Required 参数

通过传递required = True 来对指定的序列化字段进行条件的限制。

**说明:**该限制只在序列化的时候触发

同时,我们也可以对 required 条件不满足的时候抛出的异常进行第自定义:

from marshmallow import Schema, fields, ValidationErrorclass UserSchema(Schema):name = fields.String(required=True)age = fields.Integer(required=True, error_messages={"required": "Age is required."})city = fields.String(required=True,error_messages={"required": {"message": "City required", "code": 400}},)email = fields.Email()try:result = UserSchema().load({"email": "foo@bar.com"})
except ValidationError as err:print(err.messages)# {'age': ['Age is required.'],# 'city': {'code': 400, 'message': 'City required'},# 'name': ['Missing data for required field.']}

partial 参数

对应 Required 参数,如果我们在 Schema 中指定了 required=True,但是希望前端没有传入对应字段的时候,不进行 required 异常的检出,即跳过对应的字段:

1. 指定跳过缺失的字段

class UserSchema(Schema):name = fields.String(required=True)age = fields.Integer(required=True)result = UserSchema().load({"age": 42}, partial=("name",))
# OR UserSchema(partial=('name',)).load({'age': 42})
print(result)  # => {'age': 42}

2. 跳过所有缺失字段

class UserSchema(Schema):name = fields.String(required=True)age = fields.Integer(required=True)result = UserSchema().load({"age": 42}, partial=True)
# OR UserSchema(partial=True).load({'age': 42})
print(result)  # => {'age': 42}

处理未知的字段

默认的,如果接收的参数当中有 Schema 对象没有定义的字段时,默认将会抛出ValidationError。但是我们也是可以自定义它的行为。

通过 unknown 选项,其接收未知参数的处理方式将有以下三种:

  • RAISE(default):如果传入的参数没有定义的话,抛出一个异常。
  • EXCLUDE:将未指定的字段排除在外。
  • INCLUDE: 接收未定义的字段。

书写方式

在定义 Schema 时进行指定:

from marshmallow import Schema, INCLUDEclass UserSchema(Schema):class Meta:unknown = INCLUDE

在实例化Schema 对象时进行指定:

schema = UserSchema(unknown=INCLUDE)

在调用 load 方法时进行指定:

UserSchema().load(data, unknown=INCLUDE)

说明: 该三种指定方式的优先级从下到上,依次降低:

load 方式 > 实例对象传参 > Meta 当中指定

反序列化

通过 Dump 方法来进行序列化

user = User(name="Monty", email="monty@python.org")
schema = UserSchema()
result = schema.dump(user)
print(result)
# {"name": "Monty",
#  "email": "monty@python.org",
#  "created_at": "2014-08-17T14:54:16.049594+00:00"}

当然,我们也可以将数据序列化为 JSON 数据类型。

json_result = schema.dumps(user)
print(json_result)
# '{"name": "Monty", "email": "monty@python.org", "created_at": "2014-08-17T14:54:16.049594+00:00"}'

指定反序列化的内容

对于某个对象,我们并不想返回其所有的内容,这个时候我们就可以指定其返回的字段。这里我们通过 onlyexclude 参数来进行。

summary_schema = UserSchema(only=("name", "email"))
summary_schema.dump(user)
# {"name": "Monty", "email": "monty@python.org"}

反序列化为一个对象

from marshmallow import Schema, fields, post_loadclass UserSchema(Schema):name = fields.Str()email = fields.Email()created_at = fields.DateTime()@post_loaddef make_user(self, data, **kwargs):return User(**data)

在 Schema 类当中定义被 @post_load装饰的实例方法,我们便可以将待反序列化的字段转化为对应的 User 对象。

user_data = {"name": "Ronnie", "email": "ronnie@stones.com"}
schema = UserSchema()
result = schema.load(user_data)
print(result)  # => <User(name='Ronnie')>

这个方式对于操作数据库十分方便,如果我们传入的对象反序列化之后,正好是一个 Model 类型的对象,这个时候就可以直接插入到数据库当中了。

一次性反序列化多个对象

我们可以通过给定 Schema 的实例对象传入 many = True,来表示一次性序列化多个对象。

user1 = User(name="Mick", email="mick@stones.com")
user2 = User(name="Keith", email="keith@stones.com")
users = [user1, user2]
schema = UserSchema(many=True)
result = schema.dump(users)  # OR UserSchema().dump(users, many=True)
pprint(result)
# [{'name': u'Mick',
#   'email': u'mick@stones.com',
#   'created_at': '2014-08-17T14:58:57.600623+00:00'}
#  {'name': u'Keith',
#   'email': u'keith@stones.com',
#   'created_at': '2014-08-17T14:58:57.600623+00:00'}]

指定默认值

默认值的设定分为序列化和反序列化两种情况:

  • 在对应字段中指定 missing 参数,其默认效果将在反序列化的时候触发,即后端接收前端参数的时候。

  • 在对应字段中指定default参数,其默认效果将在序列化的时候触发,即后端向前端返回数据的时候。

class UserSchema(Schema):id = fields.UUID(missing=uuid.uuid1)birthdate = fields.DateTime(default=dt.datetime(2017, 9, 29))UserSchema().load({})
# {'id': UUID('337d946c-32cd-11e8-b475-0022192ed31b')}
UserSchema().dump({})
# {'birthdate': '2017-09-29T00:00:00+00:00'}

对序列化和反序列化的 key 进行指定

当我们序列化和反序列化之后返回内容中的 key 值不一致的时候,就需要data_key 这个参数来实现这一个需求。例如前端传入的字段为 emailAdress,而后端数据库当中模型的字段名为 email,这个时候我们就需要对这两者的 key 进行转化。

class UserSchema(Schema):name = fields.String()email = fields.Email(data_key="emailAddress")s = UserSchema()data = {"name": "Mike", "email": "foo@bar.com"}
result = s.dump(data)
# {'name': u'Mike',
# 'emailAddress': 'foo@bar.com'}data = {"name": "Mike", "emailAddress": "foo@bar.com"}
result = s.load(data)
# {'name': u'Mike',
# 'email': 'foo@bar.com'}

异常

注意:

schema 类中定义的字段务必写成小写类型,否则容易出现奇怪的异常。

传入数据类型错误导致异常

例如我们将前端传入的数据进行反序列化,如果传入的 key-value 并不满足 Schema 当中定义的类型,此时将会抛出 Validation error

from marshmallow import ValidationErrortry:result = UserSchema().load({"name": "John", "email": "foo"})
except ValidationError as err:print(err.messages)  # => {"email": ['"foo" is not a valid email address.']}print(err.valid_data)  # => {"name": "John"}

如果我们实例化 Schema 对象时指定 **many=True ** 的时候,此时我们传入一组待验证的数据,mashmallow 会分别对数据进行验证。并对不满足条件的数据指定编号并返回:

from marshmallow import Schema, fields, ValidationErrorclass BandMemberSchema(Schema):name = fields.String(required=True)email = fields.Email()user_data = [{"email": "mick@stones.com", "name": "Mick"},{"email": "invalid", "name": "Invalid"},  # invalid email{"email": "keith@stones.com", "name": "Keith"},{"email": "charlie@stones.com"},  # missing "name"
]try:BandMemberSchema(many=True).load(user_data)
except ValidationError as err:pprint(err.messages)# {1: {'email': ['Not a valid email address.']},#  3: {'name': ['Missing data for required field.']}}

自定义异常

当我们希望指定 validate,并根据传入的内容来进行异常的抛出的时候,就可以通过如下的方式:

from marshmallow import Schema, fields, ValidationErrordef validate_quantity(n):if n < 0:raise ValidationError("Quantity must be greater than 0.")if n > 30:raise ValidationError("Quantity must not be greater than 30.")class ItemSchema(Schema):quantity = fields.Integer(validate=validate_quantity)in_data = {"quantity": 31}
try:result = ItemSchema().load(in_data)
except ValidationError as err:print(err.messages)  # => {'quantity': ['Quantity must not be greater than 30.']}

当然,我们也可以将 validate 函数直接写到指定的 Schema 类当中,并指定需要验证的字段。

from marshmallow import fields, Schema, validates, ValidationErrorclass ItemSchema(Schema):quantity = fields.Integer()@validates("quantity")def validate_quantity(self, value):if value < 0:raise ValidationError("Quantity must be greater than 0.")if value > 30:raise ValidationError("Quantity must not be greater than 30.")

边边角角

判断是否有效

有时候,我们并不想序列化传入的参数,而只是想看下该参数是否对应指定的类型,即查看其是否有效。这个时候,我们只需要调用 validate 方法即可。

errors = UserSchema().validate({"name": "Ronnie", "email": "invalid-email"}
)
print(errors)  # {'email': ['Not a valid email address.']}

字段的隐式创建

fileds

如果传入的字段在 Python 当中已经有对应的内置数据类型,此时我们如果嫌麻烦,不想要再在 Schema 当中指定字段的类型,此时我们就可以通过 Meta当中的 fileds 参数:

class UserSchema(Schema):uppername = fields.Function(lambda obj: obj.name.upper())class Meta:fields = ("name", "email", "created_at", "uppername")

additions

还可以不用 fileds,而是使用 additions 来进行操作:

class UserSchema(Schema):uppername = fields.Function(lambda obj: obj.name.upper())class Meta:# No need to include 'uppername'additional = ("name", "email", "created_at")

有序输出

为了让序列化之后的数据顺序保持原状,此时我们可以通过 Meta 当中的 ordered 参数来进行指定。

from collections import OrderedDict
from pprint import pprintfrom marshmallow import Schema, fieldsclass UserSchema(Schema):first_name = fields.String()last_name = fields.String()email = fields.Email()class Meta:ordered = Trueu = User("Charlie", "Stones", "charlie@stones.com")
schema = UserSchema()
result = schema.dump(u)
assert isinstance(result, OrderedDict)
pprint(result, indent=2)
#  OrderedDict([('first_name', 'Charlie'),
#              ('last_name', 'Stones'),
#              ('email', 'charlie@stones.com')])

此时序列化出来的对象就不是 字典了,而是 OrderedDict。

下一篇:marshmallow——Nested

参考文章:Quickstart — marshmallow 3.12.1 documentation

marshmallow——快速入门相关推荐

  1. fastapi——简单快速入门

    fastapi--快速入门笔记 根据慕课网视频教程 地址:https://www.bilibili.com/video/BV1iN411X72b?p=36 print("\033[31m5. ...

  2. Android快速入门

    第一天 Android快速入门 第一章快速入门............................................................................. ...

  3. Shiro第一个程序:官方快速入门程序Qucickstart详解教程

    目录 一.下载解压 二.第一个Shiro程序 1. 导入依赖 2. 配置shiro配置文件 3. Quickstart.java 4. 启动测试 三.shiro.ini分析 四.Quickstart. ...

  4. 计算机入门新人必学,异世修真人怎么玩?新手快速入门必备技巧

    异世修真人怎么快速入门?最近新出来的一款文字修仙游戏,很多萌新不知道怎么玩?进小编给大家带来了游戏新手快速入门技巧攻略,希望可以帮到大家. 新手快速入门攻略 1.开局出来往下找婆婆,交互给点钱,旁边有 ...

  5. Spring Boot 2 快速教程:WebFlux 快速入门(二)

    2019独角兽企业重金招聘Python工程师标准>>> 摘要: 原创出处 https://www.bysocket.com 「公众号:泥瓦匠BYSocket 」欢迎关注和转载,保留摘 ...

  6. Apache Hive 快速入门 (CentOS 7.3 + Hadoop-2.8 + Hive-2.1.1)

    2019独角兽企业重金招聘Python工程师标准>>> 本文节选自<Netkiller Database 手札> 第 63 章 Apache Hive 目录 63.1. ...

  7. 《iOS9开发快速入门》——导读

    本节书摘来自异步社区<iOS9开发快速入门>一书中的目录,作者 刘丽霞 , 邱晓华,更多章节内容可以访问云栖社区"异步社区"公众号查看 目 录 前 言 第1章 iOS ...

  8. BIML 101 - ETL数据清洗 系列 - BIML 快速入门教程 - 序

    BIML 101 - BIML 快速入门教程 做大数据的项目,最花时间的就是数据清洗. 没有一个相对可靠的数据,数据分析就是无木之舟,无水之源. 如果你已经进了ETL这个坑,而且预算有限,并且有大量的 ...

  9. python scrapy菜鸟教程_scrapy学习笔记(一)快速入门

    安装Scrapy Scrapy是一个高级的Python爬虫框架,它不仅包含了爬虫的特性,还可以方便的将爬虫数据保存到csv.json等文件中. 首先我们安装Scrapy. pip install sc ...

  10. OpenStack快速入门

    OpenStack云计算快速入门(1) 该教程基于Ubuntu12.04版,它将帮助读者建立起一份OpenStack最小化安装.我是五岳之巅,翻译中多采用意译法,所以个别词与原版有出入,请大家谅解.我 ...

最新文章

  1. bash: /usr/lib/jvm/jdk1.7.0_80/bin/java: No such file or directory 问题
  2. 解决创建maven项目后,不能创建scala
  3. mysql mycat one_Mycat 整合 MySQL 8.x 踩坑实践
  4. 黑色背景下,将照片内封闭空心图案的空心区域染成Cyan并保存
  5. ipad xcode连接不了iPad的。
  6. 1.6编程基础之一维数组_04数组逆序重放
  7. Pandas 文本数据方法 is*()
  8. GCD之barrier
  9. php取汉字拼音首字母,php获取汉字拼音首字母的函数(真正可以使用的)
  10. 大家有用 hackerrank 刷过题吗,这家公司出了一个题来让大家测测自己有多牛
  11. 【Python】【Flask】前端调用后端方法返回页面
  12. Http请求的流程原理以及请求详解
  13. Linux中更换软件源以及更新软件过程中报错的解决方法
  14. HTML简易自适应布局
  15. 如何设计一份令人舒服的PPT,每次看都有新的idea
  16. python 输出圆的面积公式_python计算圆的面积
  17. python 列表拆分_python列表拆分
  18. 新媒体运营 | 6个自动写文案的宝藏网站,助你摆脱灵感枯竭
  19. GIS原理与技术-平时作业
  20. 定义一个学生类Student,包含三个属性姓名、年龄、性别, 创建三个学生对象存入ArrayList集合中。 A:使用迭代器遍历集合。 B:求出年龄最大的学生,然后将该对象的姓名变为:小猪佩奇。

热门文章

  1. 台式电脑怎么添加计算机硬盘,如何给台式电脑硬盘重新分区
  2. 深入分析一个经典的单片机供电电路
  3. PostgreSQL 设置远程访问
  4. 六、T100固定资产之固定资产月结处理
  5. 北大计算机科学系 97届,北大一专业:六代单传、一人旷课全系放假,毕业照只有一个人...
  6. google-services简介
  7. 关于printf的输出——进制转换
  8. 【Docker系列】 Docker secrets
  9. quantization 顶会文章简介 2017
  10. 学生成绩管理系统(合并文件,查找,总分排序,保存补考学生信息)