marshmallow——快速入门
定义一个 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"}'
指定反序列化的内容
对于某个对象,我们并不想返回其所有的内容,这个时候我们就可以指定其返回的字段。这里我们通过 only 和 exclude 参数来进行。
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——快速入门相关推荐
- fastapi——简单快速入门
fastapi--快速入门笔记 根据慕课网视频教程 地址:https://www.bilibili.com/video/BV1iN411X72b?p=36 print("\033[31m5. ...
- Android快速入门
第一天 Android快速入门 第一章快速入门............................................................................. ...
- Shiro第一个程序:官方快速入门程序Qucickstart详解教程
目录 一.下载解压 二.第一个Shiro程序 1. 导入依赖 2. 配置shiro配置文件 3. Quickstart.java 4. 启动测试 三.shiro.ini分析 四.Quickstart. ...
- 计算机入门新人必学,异世修真人怎么玩?新手快速入门必备技巧
异世修真人怎么快速入门?最近新出来的一款文字修仙游戏,很多萌新不知道怎么玩?进小编给大家带来了游戏新手快速入门技巧攻略,希望可以帮到大家. 新手快速入门攻略 1.开局出来往下找婆婆,交互给点钱,旁边有 ...
- Spring Boot 2 快速教程:WebFlux 快速入门(二)
2019独角兽企业重金招聘Python工程师标准>>> 摘要: 原创出处 https://www.bysocket.com 「公众号:泥瓦匠BYSocket 」欢迎关注和转载,保留摘 ...
- Apache Hive 快速入门 (CentOS 7.3 + Hadoop-2.8 + Hive-2.1.1)
2019独角兽企业重金招聘Python工程师标准>>> 本文节选自<Netkiller Database 手札> 第 63 章 Apache Hive 目录 63.1. ...
- 《iOS9开发快速入门》——导读
本节书摘来自异步社区<iOS9开发快速入门>一书中的目录,作者 刘丽霞 , 邱晓华,更多章节内容可以访问云栖社区"异步社区"公众号查看 目 录 前 言 第1章 iOS ...
- BIML 101 - ETL数据清洗 系列 - BIML 快速入门教程 - 序
BIML 101 - BIML 快速入门教程 做大数据的项目,最花时间的就是数据清洗. 没有一个相对可靠的数据,数据分析就是无木之舟,无水之源. 如果你已经进了ETL这个坑,而且预算有限,并且有大量的 ...
- python scrapy菜鸟教程_scrapy学习笔记(一)快速入门
安装Scrapy Scrapy是一个高级的Python爬虫框架,它不仅包含了爬虫的特性,还可以方便的将爬虫数据保存到csv.json等文件中. 首先我们安装Scrapy. pip install sc ...
- OpenStack快速入门
OpenStack云计算快速入门(1) 该教程基于Ubuntu12.04版,它将帮助读者建立起一份OpenStack最小化安装.我是五岳之巅,翻译中多采用意译法,所以个别词与原版有出入,请大家谅解.我 ...
最新文章
- bash: /usr/lib/jvm/jdk1.7.0_80/bin/java: No such file or directory 问题
- 解决创建maven项目后,不能创建scala
- mysql mycat one_Mycat 整合 MySQL 8.x 踩坑实践
- 黑色背景下,将照片内封闭空心图案的空心区域染成Cyan并保存
- ipad xcode连接不了iPad的。
- 1.6编程基础之一维数组_04数组逆序重放
- Pandas 文本数据方法 is*()
- GCD之barrier
- php取汉字拼音首字母,php获取汉字拼音首字母的函数(真正可以使用的)
- 大家有用 hackerrank 刷过题吗,这家公司出了一个题来让大家测测自己有多牛
- 【Python】【Flask】前端调用后端方法返回页面
- Http请求的流程原理以及请求详解
- Linux中更换软件源以及更新软件过程中报错的解决方法
- HTML简易自适应布局
- 如何设计一份令人舒服的PPT,每次看都有新的idea
- python 输出圆的面积公式_python计算圆的面积
- python 列表拆分_python列表拆分
- 新媒体运营 | 6个自动写文案的宝藏网站,助你摆脱灵感枯竭
- GIS原理与技术-平时作业
- 定义一个学生类Student,包含三个属性姓名、年龄、性别, 创建三个学生对象存入ArrayList集合中。 	A:使用迭代器遍历集合。 	B:求出年龄最大的学生,然后将该对象的姓名变为:小猪佩奇。