Marshmallow详解
目录
- Marshmallow详解
- 1. Scheme
- 2. Serializing(序列化)
- 3. 过滤输出
- 4. Deserializing(反序列化)
- 5. 处理多个对象的集合
- 6. Validation(验证)
- 6.1 自定义验证信息
- 6.2 将验证函数写在Schema中变成验证方法
- 6.3 Required Fields(必填选项)
- 6.4 对未知字段的处理
- 7. Schema.validate(校验数据)
- 8. Specifying Serialization/Deserialization Keys(指定序列化/反序列化键)
- 8.1 Specifying Attribute Names(序列化时指定object属性对应fields字段)
- 8.2 反序列化时指定fields字段对应object属性
- 8.3 让key同时满足序列化与反序列化的方法
- 9. 重构:创建隐式字段
- 10. 排序
- 11. “只读”与“只写”字段
- 12. 序列化/反序列化时指定字段的默认值
- 13. 后续扩展
Marshmallow详解
注意:这里的marshmallow版本是预发行版本3.x,非而不是正式版本2.x。版本3与版本2有一些差别,望周知。
文档说明:https://marshmallow.readthedocs.io
marshmallow是一个用来将复杂的orm对象与python原生数据类型之间相互转换的库,简而言之,就是实现object -> dict, objects -> list, string -> dict 和 string -> list。
序列化:序列化的意思是将数据对象转化为可存储或可传输的数据类型
反序列化:将可存储或可传输的数据类型转化为数据对象
要进行序列化或反序列化,首先我们需要一个用来操作的object,这里我们先定义一个类:
import datetime as dtclass User:def __init__(self, name, email):self.name = nameself.email = emailself.created_time = dt.datetime.now()
1. Scheme
要对一个类或者一个json数据实现相互转换(即序列化和反序列化), 需要一个中间载体, 这个载体就是Schema,另外Schema还可以用来做数据验证。
# 这是一个简单的Scheme
from marshmallow import Schema, fieldsclass UserSchema(Schema):name = fields.String()email = fields.Email()created_time = fields.DateTime()
2. Serializing(序列化)
使用scheme的dump()方法来序列化对象,返回的是dict格式的数据
另外schema的dumps()方法序列化对象,返回的是json编码格式的字符串。
user = User(name="TTY", email="tty@python.org")
schema = UserSchema()
res = schema.dump(user)
print(res)
# {'email': 'tty@python.org', 'name': 'TTY', 'created_time': '2019-08-05T14:43:51.168241+00:00'}res2 = schema.dumps(user)
print(res2)
# '{"name": "TTY", "created_time": "2019-08-05T14:46:07.111755+00:00", "email": "tty@python.org"}'
3. 过滤输出
当不需要输出所有的字段时,可以在实例化Scheme时,声明only参数,来指定输出:
summary_schema = UserSchema(only=("name", "email"))
res = summary_schema.dump(user)
print(res)
{'name': 'TTY', 'email': 'tty@python.org'}
only参数用来指定输出的字段,也可以用exclude参数来排除不输出的字段,达到一样的效果。
4. Deserializing(反序列化)
schema的load()方法与dump()方法相反,用于dict类型的反序列化。他将输入的字典格式数据转换成应用层数据结构。他也能起到验证输入的字典格式数据的作用。
同样,也有对json解码的loads()方法。用于string类型的反序列化。
默认情况下,load()方法返回一个字典,当输入的数据的值不匹配字段类型时,抛出 ValidationError 异常。
schema = UserSchema()
res = schema.load(user_data)
print(res)
# {'email': 'tty2@python.org', 'created_time': datetime.datetime(2019, 8, 5, 14, 46, 7), 'name': 'tty2'}
对反序列化而言, 将传入的dict变成object更加有意义. 在Marshmallow中, dict -> object的方法需要自己实现, 然后在该方法前面加上一个装饰器post_load即可
class UserSchema(Schema):name = fields.String()email = fields.Email()created_time = fields.DateTime()@post_loaddef make_user(self, data):return User(**data)
这样每次调用load()方法时, 会按照make_user的逻辑, 返回一个User类对象。
user_data = {"name": "tty2","email": "tty2@python.org"
}schema = UserSchema()
res = schema.load(user_data)
print(res)
# <__main__.User object at 0x0000027BE9678128>
user = res
print("name: {} email: {}".format(user.name, user.email))
# name: tty2 email: tty2@python.org
5. 处理多个对象的集合
多个对象的集合如果是可迭代的,那么也可以直接对这个集合进行序列化或者反序列化。在实例化Scheme类时设置参数many=True
也可以不在实例化类的时候设置,而在调用dump()方法的时候传入这个参数。
user1 = User(name="tty1", email="tty1@python.org")
user2 = User(name="tty2", email="tty2@python.org")
users = [user1, user2]# 第一种方法
schema = UserSchema(many=True)
res = schema.dump(users)# 第二种方法
# schema = UserSchema()
# res = schema.dump(users, many=True)print(res)
# [{'created_time': '2019-08-05T15:09:19.781325+00:00', 'email': 'tty1@python.org', 'name': 'tty1'},
# {'created_time': '2019-08-05T15:09:19.781325+00:00', 'email': 'tty2@python.org', 'name': 'tty2'}]
6. Validation(验证)
当不合法的数据通过Schema.load()或者Schema.loads()时,会抛出一个 ValidationError 异常。ValidationError.messages属性有验证错误信息,验证通过的数据在 ValidationError.valid_data 属性中
我们捕获这个异常,然后做异常处理。首先需要导入ValidationError这个异常
```python
from marshmallow import Schema, fields, ValidationError
class UserSchema(Schema):
name = fields.String()
email = fields.Email()
created_time = fields.DateTime()
try:
res = UserSchema().load({"name": "ttty", "email": "ttty"})
except ValidationError as e:
print("错误信息:{} 合法数据:{}".format(e.messages, e.valid_data))
# 错误信息:{'email': ['Not a valid email address.']} 合法数据:{'name': 'ttty'}
``
当验证一个数据集合的时候,返回的错误信息会以 错误序号-错误信息 的键值对形式保存在errors中
user_data = [{'email': 'mick@stones.com', 'name': 'Mick'},{'email': 'invalid', 'name': 'Invalid'},{'name': 'Keith'},{'email': 'charlie@stones.com'},
]
try:schema = UserSchema(many=True)res = schema.load(user_data)
except ValidationError as e:print("错误信息:{} 合法数据:{}".format(e.messages, e.valid_data))# 错误信息:{1: {'email': ['Not a valid email address.']}}# 合法数据:[{'email': 'mick@stones.com', 'name': 'Mick'},# {'name': 'Invalid'},# {'name': 'Keith'},# {'email': 'charlie@stones.com'}]
可以看到上面,有错误信息,但是对于没有传入的属性则没有检查,也就是说没有规定属性必须传入。
在Schema里规定不可缺省字段:设置参数required=True
class UserSchema(Schema):name = fields.String(required=True)email = fields.Email()created_time = fields.DateTime()
再次进行验证:
try:schema = UserSchema(many=True)res = schema.load(user_data)
except ValidationError as e:print("错误信息:{} 合法数据:{}".format(e.messages, e.valid_data))# 错误信息:{1: {'email': ['Not a valid email address.']},# 3: {'name': ['Missing data for required field.']}}# 合法数据:[{'email': 'mick@stones.com', 'name': 'Mick'},# {'name': 'Invalid'},# {'name': 'Keith'},# {'email': 'charlie@stones.com'}]
6.1 自定义验证信息
在编写Schema类的时候,可以向内建的fields中设置validate参数的值来定制验证的逻辑, validate的值可以是函数, 匿名函数lambda, 或者是定义了__call__的对象。
class UserSchema(Schema):name = fields.String(required=True, validate=lambda s: len(s)<6)email = fields.Email()created_time = fields.DateTime()user_data = {'name': 'InvalidName', 'email': 'tty@python.org'}
try:res = UserSchema().load(user_data)
except ValidationError as e:print(e.messages)# {'name': ['Invalid value.']}
在验证函数中自定义异常信息:
from marshmallow import Schema, fields, ValidationErrordef validate_name(name):if len(name) <= 2:raise ValidationError("name长度必须大于2位")if len(name) >= 6:raise ValidationError("name长度不能大于6位")class UserSchema(Schema):name = fields.String(required=True, validate=validate_name)email = fields.Email()created_time = fields.DateTime()user_data = {'name': 'InvalidName', 'email': 'tty@python.org'}
try:res = UserSchema().load(user_data)
except ValidationError as e:print(e.messages)# {'name': ['name长度不能大于6位']}
注意:只会在反序列化的时候发生验证!序列化的时候不会验证!
6.2 将验证函数写在Schema中变成验证方法
在Schema中,使用validates装饰器就可以注册验证方法。
from marshmallow import Schema, fields, ValidationError, validatesclass UserSchema(Schema):name = fields.String(required=True)email = fields.Email()created_time = fields.DateTime()@validates("name")def validate_name(self, value):if len(value) <= 2:raise ValidationError("name长度必须大于2位")if len(value) >= 6:raise ValidationError("name长度不能大于6位")user_data = {'name': 'InvalidName', 'email': 'tty@python.org'}
try:res = UserSchema().load(user_data)
except ValidationError as e:print(e.messages)# {'name': ['name长度不能大于6位']}
6.3 Required Fields(必填选项)
上面已经简单使用过required参数了。这里再简单介绍一下。
自定义required异常信息:
首先我们可以自定义在requird=True时缺失字段时抛出的异常信息:设置参数error_messages的值
class UserSchema(Schema):name = fields.String(required=True, error_messages={"required": "name字段必须填写"})email = fields.Email()created_time = fields.DateTime()user = {"email": "tty@python.org"}
schema = UserSchema()
try:res = schema.load(user)
except ValidationError as e:print(e.messages)# {'name': ['name字段必须填写']}
忽略部分字段:
使用required之后我们还是可以在传入数据的时候忽略这个必填字段。
class UserSchema(Schema):name = fields.String(required=True)age = fields.Integer(required=True)# 方法一:在load()方法设置partial参数的值(元组),表时忽略那些字段。
schema = UserSchema()
res = schema.load({"age": 42}, partial=("name",))
print(res)
# {'age': 42}# 方法二:直接设置partial=True
schema = UserSchema()
res = schema.load({"age": 42}, partial=True)
print(res)
# {'age': 42}
看起来两种方法是一样的,但是方法一和方法二有区别:方法一只忽略传入partial的字段,方法二会忽略除前面传入的数据里已有的字段之外的所有字段
6.4 对未知字段的处理
默认情况下,如果传入了未知的字段(Schema里没有的字段),执行load()方法会抛出一个 ValidationError 异常。这种行为可以通过更改 unknown 选项来修改。
unknown 有三个值:
- EXCLUDE: exclude unknown fields(直接扔掉未知字段)
- INCLUDE: accept and include the unknown fields(接受未知字段)
- RAISE: raise a ValidationError if there are any unknown fields(抛出异常)
我们可以看到,默认的行为就是RAISE。有两种方法去更改:
方法一:在编写Schema类的时候在class Meta里修改
# 首先导入 EXCLUDE
from marshmallow import EXCLUDEclass UserSchema(Schema):name = fields.String(required=True, error_messages={"required": "name字段必须填写"})email = fields.Email()created_time = fields.DateTime()class Meta:unknown = EXCLUDE
方法二:在实例化Schema类的时候设置参数unknown的值
class UserSchema(Schema):name = fields.Str(required=True, error_messages={"required": "name字段必须填写"})email = fields.Email()created_time = fields.DateTime()shema = UserSchema(unknown=EXCLUDE)
7. Schema.validate(校验数据)
如果只是想用Schema去验证数据, 而不进行反序列化生成对象, 可以使用Schema.validate()
可以看到, 通过schema.validate()会自动对数据进行校验, 如果有错误, 则会返回错误信息的dict,没有错误则返回空的dict,通过返回的数据, 我们就可以确认验证是否通过.
class UserSchema(Schema):name = fields.Str(required=True, error_messages={"required": "name字段必须填写"})email = fields.Email()created_time = fields.DateTime()user = {"name": "tty", "email": "tty@python"}
schema = UserSchema()
res = schema.validate(user)
print(res)
# {'email': ['Not a valid email address.']}user1 = {"name": "tty", "email": "tty@python.org"}
schema = UserSchema()
res1 = schema.validate(user1)
print(res1)
# {}
8. Specifying Serialization/Deserialization Keys(指定序列化/反序列化键)
8.1 Specifying Attribute Names(序列化时指定object属性对应fields字段)
Schema默认会序列化传入对象和自身定义的fields相同的属性, 然而你也会有需求使用不同的fields和属性名. 在这种情况下, 你需要明确定义这个fields将从什么属性名取值
import datetime as dt
from marshmallow import Schema, fieldsclass User:def __init__(self, name, email):self.name = nameself.email = emailself.created_time = dt.datetime.now()class UserSchema(Schema):full_name = fields.String(attribute="name")email_address = fields.Email(attribute="email")created_at = fields.DateTime(attribute="created_time")user = User("ttty", email="ttty@python.org")
schema = UserSchema()
res = schema.dump(user)
print(res)
如上所示:UserSchema中的full_name,email_address,created_at分别从User对象的name,email,created_time属性取值。
8.2 反序列化时指定fields字段对应object属性
这个与上面相反,Schema默认反序列化传入字典和输出字典中相同的字段名. 如果你觉得数据不匹配你的schema, 可以传入load_from参数指定需要增加load的字段名(原字段名也能load, 且优先load原字段名)
class UserSchema(Schema):full_name = fields.String(load_from="name")email_address = fields.Email(load_from="email")created_at = fields.DateTime(load_from="created_time")user = {"full_name": "ttty", "email_address": "ttty@python.org"}
schema = UserSchema()
res = schema.load(user)
print(res)
# {'email_address': 'ttty@python.org', 'full_name': 'ttty'}
8.3 让key同时满足序列化与反序列化的方法
class UserSchema(Schema):full_name = fields.String(data_key="name")email_address = fields.Email(data_key="email")created_at = fields.DateTime(data_key="created_time")# 序列化
user = {"full_name": "ttty", "email_address": "ttty@python.org"}
schema = UserSchema()
res = schema.dump(user)
print(res)
# {'name': 'ttty', 'email': 'ttty@python.org'}# 反序列化
user1 = {"name": "ttty", "email": "ttty@python.org"}
schema = UserSchema()
res = schema.load(user1)
print(res)
# {'email_address': 'ttty@python.org', 'full_name': 'ttty'}
9. 重构:创建隐式字段
当Schema具有许多属性时,为每个属性指定字段类型可能会重复,特别是当许多属性已经是本地python的数据类型时。class Meta允许指定要序列化的属性,marshmallow将根据属性的类型选择适当的字段类型。
# 重构Schema
class UserSchema(Schema):uppername = fields.Function(lambda obj: obj.name.upper())class Meta:fields = ("name", "email", "created_at", "uppername")
以上代码中, name将自动被格式化为String类型,created_at将被格式化为DateTime类型。
如果您希望指定除了显式声明的字段之外还包括哪些字段名,则可以使用附加选项。如下:
class UserSchema(Schema):uppername = fields.Function(lambda obj: obj.name.upper())class Meta:# No need to include 'uppername'additional = ("name", "email", "created_at")
10. 排序
对于某些用例,维护序列化输出的字段顺序可能很有用。要启用排序,请将ordered选项设置为true。这将指示marshmallow将数据序列化到collections.OrderedDict
from collections import OrderedDictclass User:def __init__(self, name, email):self.name = nameself.email = emailself.created_time = dt.datetime.now()class UserSchema(Schema):uppername = fields.Function(lambda obj: obj.name.upper())class Meta:fields = ("name", "email", "created_time", "uppername")ordered = Trueu = User("Charlie", "charlie@stones.com")
schema = UserSchema()
res = schema.dump(u)
print(isinstance(res, OrderedDict))
# True
print(res)
# OrderedDict([('name', 'Charlie'), ('email', 'charlie@stones.com'), ('created_time', '2019-08-05T20:22:05.788540+00:00'), ('uppername', 'CHARLIE')])
11. “只读”与“只写”字段
在Web API的上下文中,序列化参数dump_only和反序列化参数load_only在概念上分别等同于只读和只写字段。
class UserSchema(Schema):name = fields.Str()# password is "write-only"password = fields.Str(load_only=True)# created_at is "read-only"created_at = fields.DateTime(dump_only=True)
load时,dump_only字段被视为未知字段。如果unknown选项设置为include,则与这些字段对应的键的值将因此loaded而不进行验证。
12. 序列化/反序列化时指定字段的默认值
序列化时输入值缺失用default指定默认值。反序列化时输入值缺失用missing指定默认值。
class UserSchema(Schema):id = fields.UUID(missing=uuid.uuid1)birthdate = fields.DateTime(default=dt.datetime(2020, 9, 9))# 序列化
res1 = UserSchema().dump({})
print(res1)
# {'birthdate': '2020-09-09T00:00:00+00:00'}# 反序列化
res = UserSchema().load({})
print(res)
# {'id': UUID('18f1eb3a-b7ec-11e9-82fb-8cec4b76ee65')}
13. 后续扩展
需要表示对象之间的关系?请参见Nesting Schemas 页面。
想要创建自己的字段类型?请参阅自定义字段页面。
需要添加模式级验证,后处理或错误处理行为吗?请参阅Schema扩展页面。
例如,使用marshmallow的应用程序,请查看Examples页面。
一个自定义字段的小例子:
from marshmallow import Schema, fieldsclass String128(fields.String):"""长度为128的字符串类型"""default_error_messages = {"type": "该字段只能是字符串类型","invalid": "该字符串长度必须大于6",}def _deserialize(self, value, attr, data, **kwargs):if not isinstance(value, str):self.fail("type")if len(value) < 6:self.fail("invalid")class AppSchema(Schema):name = String128(required=True)priority = fields.Integer()obj_type = String128()link = String128()deploy = fields.Dict()description = fields.String()projects = fields.List(cls_or_instance=fields.Dict)app = {"name": "app11","priority": 2,"obj_type": "web","link": "123.123.00.2","deploy": {"deploy1": "deploy1", "deploy2": "deploy2"},"description": "app111 test111","projects": [{"id": 2}]
}schema = AppSchema()
res = schema.validate(app)
print(res)
# {'obj_type': ['该字符串长度必须大于6'], 'name': ['该字符串长度必须大于6']}
转载于:https://www.cnblogs.com/ChangAn223/p/11305376.html
Marshmallow详解相关推荐
- 一加一 安装 Kali NetHunter 详解
一加一 安装 Kali NetHunter 详解# 2018年4月20日13:02:44 手机:Oneplus one 软件: Kali NetHunter 工具:一加工具包 解读:手机安装 kali ...
- RESTful 详解
1. 什么是REST REST全称是Representational State Transfer,中文意思是表述(编者注:通常译为表征)性状态转移. 它首次出现在2000年Roy Fielding的 ...
- 从命令行到IDE,版本管理工具Git详解(远程仓库创建+命令行讲解+IDEA集成使用)
首先,Git已经并不只是GitHub,而是所有基于Git的平台,只要在你的电脑上面下载了Git,你就可以通过Git去管理"基于Git的平台"上的代码,常用的平台有GitHub.Gi ...
- JVM年轻代,老年代,永久代详解
秉承不重复造轮子的原则,查看印象笔记分享连接↓↓↓↓ 传送门:JVM年轻代,老年代,永久代详解 速读摘要 最近被问到了这个问题,解释的不是很清晰,有一些概念略微模糊,在此进行整理和记录,分享给大家.在 ...
- docker常用命令详解
docker常用命令详解 本文只记录docker命令在大部分情境下的使用,如果想了解每一个选项的细节,请参考官方文档,这里只作为自己以后的备忘记录下来. 根据自己的理解,总的来说分为以下几种: Doc ...
- 通俗易懂word2vec详解词嵌入-深度学习
https://blog.csdn.net/just_so_so_fnc/article/details/103304995 skip-gram 原理没看完 https://blog.csdn.net ...
- 深度学习优化函数详解(5)-- Nesterov accelerated gradient (NAG) 优化算法
深度学习优化函数详解系列目录 深度学习优化函数详解(0)– 线性回归问题 深度学习优化函数详解(1)– Gradient Descent 梯度下降法 深度学习优化函数详解(2)– SGD 随机梯度下降 ...
- CUDA之nvidia-smi命令详解---gpu
nvidia-smi是用来查看GPU使用情况的.我常用这个命令判断哪几块GPU空闲,但是最近的GPU使用状态让我很困惑,于是把nvidia-smi命令显示的GPU使用表中各个内容的具体含义解释一下. ...
- Bert代码详解(一)重点详细
这是bert的pytorch版本(与tensorflow一样的,这个更简单些,这个看懂了,tf也能看懂),地址:https://github.com/huggingface/pytorch-pretr ...
- CRF(条件随机场)与Viterbi(维特比)算法原理详解
摘自:https://mp.weixin.qq.com/s/GXbFxlExDtjtQe-OPwfokA https://www.cnblogs.com/zhibei/p/9391014.html C ...
最新文章
- Android system server之PackageManagerService详细分析
- 为啥不装杀毒软件?全家桶太多...
- 如何用VC++开发读取网卡MAC地址的程序
- CSS 七层叠层顺序(stacking level)
- $(#id).val()取值textarea是
- 二维高斯曲面拟合法求取光斑中心及算法的C++实现
- linux装Oracle需要jdk,告诉你Ubuntu Linux下安装Oracle JDK的方法及命令
- 大数据_Flink_Java版_ProcessFunction(4)_应用案例_高低温分流---Flink工作笔记0069
- nginx使用ssl证书
- ThinkPad特有设计和特色软件
- tcp压测工具_使用BeetleX的TcpBenchmark工具进行百万设备模拟测试
- 【国产MCU移植】看看有没有你需要的,一起来查漏补缺吧!(附已报名的硬件)...
- html css纯写桌球运动轨迹,纯JS实现椭圆轨迹运动的代码
- 计算机中丢失glut.dll,OpenGl的源程序,运行就提示,计算机丢失 glut32.dll文件
- 确实有必要好好学英语
- 【Mybatis学习路线】day02mybatis的增删改查操作
- 2021-2027全球与中国巡航型摩托车市场现状及未来发展趋势
- 什么叫北上资金,北向资金什么意思?
- HDU 6438 Buy and Resell【贪心】
- 人工神经网络基本构成有哪些,具有什么特征