资讯专栏INFORMATION COLUMN

marshmallow快速上手

jhhfft / 427人阅读

摘要:方法对应的是方法,它反序列化一个字典为数据结构。某些例如和内置了验证器验证集合时,错误字典将基于无效字段的索引作为键通过给的参数传递对象,可以执行额外的验证验证函数可以返回布尔值或抛出异常。

快速上手 Declaring Schemas

首先创建一个基础的user“模型”(只是为了演示,并不是真正的模型):

import datetime as dt

class User(object):
    def __init__(self, name, email):
        self.name = name
        self.email = email
        self.created_at = dt.datetime.now()

    def __repr__(self):
        return "".format(self=self)

然后通过定义一个映射属性名称到Field对象的类创建schema

from marshmallow import Schema, fields

class UserSchema(Schema):
    name = fields.Str()
    email = fields.Email()
    created_at = fields.DateTime()
Serializing Objects ("Dumping")

传递对象到创建的schema的dump方法,返回一个序列化字典对象(和一个错误字典对象,下文讲):

from marshmallow import pprint

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

也可以使用dumps方法序列化对象为JSON字符串:

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

使用only参数指定要序列化输出的字段:

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

使用exclude参数指定不进行序列化输出的字段。

Deserializing Objects ("Loading")

dump方法对应的是load方法,它反序列化一个字典为python数据结构。

load方法默认返回一个fields字段和反序列化值对应的字典对象:

from pprint import pprint

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

Schema子类中定义一个方法并用post_load装饰,该方法接收一个要反序列化的数据字典返回原始python对象:

from marshmallow import Schema, fields, post_load

class UserSchema(Schema):
    name = fields.Str()
    email = fields.Email()
    created_at = fields.DateTime()

    @post_load
    def make_user(self, data):
        return User(**data)

现在调用load方法将返回一个User对象:

user_data = {
    "name": "Ronnie",
    "email": "ronnie@stones.com"
}
schema = UserSchema()
result = schema.load(user_data)
result.data  # => 
Handling Collections of Objects

可迭代的对象集合也可以进行序列化和反序列化。只需要设置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)
result.data
# [{"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"}]
Validation

Schema.load()Schema.loads()返回值的第二个元素是一个验证错误的字典。某些fields例如EmailURL内置了验证器:

data, errors = UserSchema().load({"email": "foo"})
errors  # => {"email": [""foo" is not a valid email address."]}
# OR, equivalently
result = UserSchema().load({"email": "foo"})
result.errors  # => {"email": [""foo" is not a valid email address."]}

验证集合时,错误字典将基于无效字段的索引作为键:

class 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"
]

result = BandMemberSchema(many=True).load(user_data)
result.errors
# {1: {"email": [""invalid" is not a valid email address."]},
#  3: {"name": ["Missing data for required field."]}}

通过给fields的validate参数传递callable对象,可以执行额外的验证:

class ValidatedUserSchema(UserSchema):
    # NOTE: This is a contrived example.
    # You could use marshmallow.validate.Range instead of an anonymous function here
    age = fields.Number(validate=lambda n: 18 <= n <= 40)

in_data = {"name": "Mick", "email": "mick@stones.com", "age": 71}
result = ValidatedUserSchema().load(in_data)
result.errors  # => {"age": ["Validator (71.0) is False"]}

验证函数可以返回布尔值或抛出ValidationError异常。如果是抛出异常,其信息将保存在错误字典中:

from marshmallow import Schema, fields, ValidationError

def 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}
result, errors = ItemSchema().load(in_data)
errors  # => {"quantity": ["Quantity must not be greater than 30."]}
Field Validators as Methods

使用validates装饰器注册方法验证器:

from marshmallow import fields, Schema, validates, ValidationError

class 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.")
strict Mode

在schema构造器或class Meta中设置strict=True,遇到不合法数据时将抛出异常,通过ValidationError.messages属性可以访问验证错误的字典:

from marshmallow import ValidationError

try:
    UserSchema(strict=True).load({"email": "foo"})
except ValidationError as err:
    print(err.messages)# => {"email": [""foo" is not a valid email address."]}
Required Fields

设置required=True可以定义一个必要字段,调用Schema.load()方法时如果字段值缺失将验证失败并保存错误信息。

error_messages参数传递一个dict对象可以自定义必要字段的错误信息:

class 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()

data, errors = UserSchema().load({"email": "foo@bar.com"})
errors
# {"name": ["Missing data for required field."],
#  "age": ["Age is required."],
#  "city": {"message": "City required", "code": 400}}
Partial Loading

通过指定partial参数,可以忽略某些缺失字段的required检查:

class UserSchema(Schema):
    name = fields.String(required=True)
    age = fields.Integer(required=True)

data, errors = UserSchema().load({"age": 42}, partial=("name",))
# OR UserSchema(partial=("name",)).load({"age": 42})
data, errors  # => ({"age": 42}, {})

或者设置partial=True忽略所有缺失字段的required检查:

class UserSchema(Schema):
    name = fields.String(required=True)
    age = fields.Integer(required=True)

data, errors = UserSchema().load({"age": 42}, partial=True)
# OR UserSchema(partial=True).load({"age": 42})
data, errors  # => ({"age": 42}, {})
Schema.validate

使用Schema.validate()可以只验证输入数据而不反序列化:

errors = UserSchema().validate({"name": "Ronnie", "email": "invalid-email"})
errors  # {"email": [""invalid-email" is not a valid email address."]}
Specifying Attribute Names

默认情况下schema序列化处理和field名称相同的对象属性。对于属性和field不相同的场景,通过attribute参数指定field处理哪个属性:

class UserSchema(Schema):
    name = fields.String()
    email_addr = fields.String(attribute="email")
    date_created = fields.DateTime(attribute="created_at")

user = User("Keith", email="keith@stones.com")
ser = UserSchema()
result, errors = ser.dump(user)
pprint(result)
# {"name": "Keith",
#  "email_addr": "keith@stones.com",
#  "date_created": "2014-08-17T14:58:57.600623+00:00"}
Specifying Deserialization Keys

默认情况下schema反序列化处理键和field名称相同的字典。可以通过load_from参数指定额外处理的字典键值:

class UserSchema(Schema):
    name = fields.String()
    email = fields.Email(load_from="emailAddress")

data = {
    "name": "Mike",
    "emailAddress": "foo@bar.com"
}
s = UserSchema()
result, errors = s.load(data)
#{"name": u"Mike",
# "email": "foo@bar.com"}
Specifying Serialization Keys

如果要序列化输出不想使用field名称作为键,可以通过dump_to参数指定(和load_from相反):

class UserSchema(Schema):
    name = fields.String(dump_to="TheName")
    email = fields.Email(load_from="CamelCasedEmail", dump_to="CamelCasedEmail")

data = {
    "name": "Mike",
    "email": "foo@bar.com"
}
s = UserSchema()
result, errors = s.dump(data)
#{"TheName": u"Mike",
# "CamelCasedEmail": "foo@bar.com"}
Refactoring: Implicit Field Creation

当schema中有很多属性时,为每个属性指定field类型会产生大量的重复工作,尤其是大部分属性为原生的python数据类型时。

class Meta允许开发人员指定序列化哪些属性,Marshmallow会基于属性类型选择合适的field类型:

# 重构UserSchema
class UserSchema(Schema):
    uppername = fields.Function(lambda obj: obj.name.upper())

    class Meta:
        fields = ("name", "email", "created_at", "uppername")


user = User(name="erika", email="marshmallow@126.com")
schema = UserSchema()
result = schema.dump(user)
print(result.data)

# {"created_at": "2019-05-20T15:45:27.760000+00:00", "uppername": "ERIKA", "name": "erika", "email": "marshmallow@126.com"}

除了显式声明的field外,使用additional选项可以指定还要包含哪些fields。以下代码等同于上面的代码:

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

设置ordered=True可以维护序列化输出的field顺序,此时序列化字典为collections.OrderedDict类型:

from collections import OrderedDict

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

u = User("Charlie", "charlie@stones.com")
schema = UserSchema()
result = schema.dump(u)
assert isinstance(result.data, OrderedDict)
# marshmallow"s pprint function maintains order
pprint(result.data, indent=2)
# {
#   "name": "Charlie",
#   "email": "charlie@stones.com",
#   "created_at": "2014-10-30T08:27:48.515735+00:00",
#   "uppername": "CHARLIE"
# }
"Read-only" and "Write-only" Fields

在web API上下文中,dump_onlyload_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)
更多教程

marshmallow之schema嵌套
marshmallow之自定义Field
marshmallow之Schema延伸功能

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/43841.html

相关文章

  • marshmallow之自定义Field

    摘要:有三种方式创建自定义的。下面的例子判断某个对象是否是某个对象的作者,以及的属性是否出现单词自定义错误信息字段验证产生的错误信息可以在类级别或实例级别配置。在类级别时,可以定义为错误码和错误信息的字典映射在类实例化时,给参数传参对象 有三种方式创建自定义的field。 创建Field类的子类 创建继承自marshmallow.fields.Field类的子类并实现_serialize和/...

    AWang 评论0 收藏0
  • marshmallow之Schema延伸功能

    摘要:创建实例时如果传递了,表示需要接收输入数据集合,装饰器注册预处理和后处理方法时需要传递参数。 预处理和后处理方法 数据的预处理和后处理方法通过pre_load, post_load, pre_dump和post_dump装饰器注册: from marshmallow import Schema, fields, pre_load class UserSchema(Schema): ...

    hzx 评论0 收藏0
  • marshmallow之schema嵌套

    摘要:嵌套可以嵌套使用以表示对象间的关系如外键关系。在下面的例子中,和对象是一对多的关系必须使用或参数避免无限递归也可以使用导入模块的方式传递嵌套,如自嵌套给传递字符串参数表示和对象本身的关系 schema嵌套 schema可以嵌套使用以表示对象间的关系(如外键关系)。 例如下例中Blog有一个用User对象表示的author属性: import datetime as dt class ...

    miracledan 评论0 收藏0
  • SegmentFault 技术周刊 Vol.4 - 这份 Android 有点甜

    摘要:阅读本期周刊,你将快速入门,开启甜蜜之旅。然则的原理负责发送以及处理消息,创建消息队列并不断从队列中取出消息交给,则用于保存消息。 showImg(/img/bVCN99?w=900&h=385); 2016 年 8 月,Android 7.0 Nougat(牛轧糖)正式发布,那么问题来了,你 Marshmallow 了么(¬ -̮ ¬) Cupcake、Donut、Gingerbre...

    jay_tian 评论0 收藏0

发表评论

0条评论

jhhfft

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<