首先建立一個基礎的user「模型」(只是爲了演示,並非真正的模型):python
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 '<User(name={self.name!r})>'.format(self=self)
而後經過定義一個映射屬性名稱到Field
對象的類建立schema
:web
from marshmallow import Schema, fields class UserSchema(Schema): name = fields.Str() email = fields.Email() created_at = fields.DateTime()
傳遞對象到建立的schema的dump
方法,返回一個序列化字典對象(和一個錯誤字典對象,下文講):json
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字符串:segmentfault
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"}'
使用only
參數指定要序列化輸出的字段:數據結構
summary_schema = UserSchema(only=('name', 'email')) summary_schema.dump(user).data # {"name": "Monty Python", "email": "monty@python.org"}
使用exclude
參數指定不進行序列化輸出的字段。函數
dump方法對應的是load
方法,它反序列化一個字典爲python數據結構。post
load方法默認返回一個fields
字段和反序列化值對應的字典對象:ui
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)}
在Schema
子類中定義一個方法並用post_load
裝飾,該方法接收一個要反序列化的數據字典返回原始python對象:code
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對象:orm
user_data = { 'name': 'Ronnie', 'email': 'ronnie@stones.com' } schema = UserSchema() result = schema.load(user_data) result.data # => <User(name='Ronnie')>
可迭代的對象集合也能夠進行序列化和反序列化。只須要設置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'}]
Schema.load()
和Schema.loads()
返回值的第二個元素是一個驗證錯誤的字典。某些fields例如Email
和URL
內置了驗證器:
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 <lambda>(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.']}
使用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.')
在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=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
參數,能夠忽略某些缺失字段的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()
能夠只驗證輸入數據而不反序列化:
errors = UserSchema().validate({'name': 'Ronnie', 'email': 'invalid-email'}) errors # {'email': ['"invalid-email" is not a valid email address.']}
默認狀況下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'}
默認狀況下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'}
若是要序列化輸出不想使用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'}
當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")
設置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" # }
在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)
marshmallow之schema嵌套
marshmallow之自定義Field
marshmallow之Schema延伸功能