python序列化_marshmallow

Marshmallow

官方文档:https://marshmallow.readthedocs.io/en/latest/

Marshmallow,中文译作:棉花糖。是一个轻量级的数据格式转换的模块,也叫序列化和反序列化模块,常用于将复杂的orm模型对象与python原生数据类型之间相互转换。marshmallow提供了丰富的api功能。如下:

  1. Serializing

    序列化[可以把数据对象转化为可存储或可传输的数据类型,例如:objects/object->list/dict,dict/list->string]

  2. Deserializing

    反序列化器[把可存储或可传输的数据类型转换成数据对象,例如:list/dict->objects/object,string->dict/list]

  3. Validation

    数据校验,可以在反序列化阶段,针对要转换数据的内容进行类型验证或自定义验证。

Marshmallow本身是一个单独的库,基于我们当前项目使用框架是flask并且数据库ORM框架使用SQLAlchemy,所以我们可以通过安装flask-sqlalchemy和marshmallow-sqlalchemy集成到项目就可以了。

基本安装和配置

模块安装:

pip install -U marshmallow-sqlalchemy -i https
pip install -U flask-sqlalchemy
pip install -U flask-marshmallow

模块初始化:

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_marshmallow import Marshmallow

from datetime import datetime


app = Flask(__name__)
app.config["SQLALCHEMY_DATABASE_URI"]="mysql://root:123@127.0.0.1:3306/mofangapp?charset=utf8mb4"
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
db = SQLAlchemy()
ma = Marshmallow()
db.init_app(app)
ma.init_app(app)

class User(db.Model):
    __tablename__ = "tb_user"
    id = db.Column(db.Integer, primary_key=True, comment="主键ID")
    username = db.Column(db.String(255), index=True, comment="用户名")
    password = db.Column(db.String(255), comment="登录密码")
    mobile = db.Column(db.String(15), index=True, comment="手机号码")
    sex = db.Column(db.Boolean, default=True, comment="性别")
    email = db.Column(db.String(255), index=True, comment="邮箱")
    created_time = db.Column(db.DateTime, default=datetime.now, comment="创建时间")
    updated_time = db.Column(db.DateTime, default=datetime.now, onupdate=datetime.now, comment="更新时间")

    def __repr__(self):
        return "<%s: %s>" % (self.__class__.name,self.username)



@app.route("/")
def index():
    return ""

if __name__ == '__main__':
    # with app.app_context():
    #     db.drop_all()
    #     db.create_all()
    app.run(debug=True,port=6000)

基本构造器(Schema)

marshmallow转换数据格式主要通过构造器类来完成。在marshmallow使用过程中所有的构造器类必须直接或间接继承于Schema基类,而Schema类提供了数据转换的基本功能:序列化,验证和反序列化。

基于Schema完成数据序列化转换

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_marshmallow import Marshmallow

from datetime import datetime

app = Flask(__name__)
app.config["SQLALCHEMY_DATABASE_URI"]="mysql://root:123@127.0.0.1:3306/mofangapp?charset=utf8mb4"
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
db = SQLAlchemy()
ma = Marshmallow()
db.init_app(app)
ma.init_app(app)

class User(db.Model):
    __tablename__ = "tb_user"
    id = db.Column(db.Integer, primary_key=True, comment="主键ID")
    username = db.Column(db.String(255), index=True, comment="用户名")
    password = db.Column(db.String(255), comment="登录密码")
    mobile = db.Column(db.String(15), index=True, comment="手机号码")
    age = db.Column(db.SmallInteger, comment="年龄")
    sex = db.Column(db.Boolean, default=True, comment="性别")
    email = db.Column(db.String(255), index=True, comment="邮箱")
    created_time = db.Column(db.DateTime, default=datetime.now, comment="创建时间")
    updated_time = db.Column(db.DateTime, default=datetime.now, onupdate=datetime.now, comment="更新时间")

    def __repr__(self):
        return "<%s: %s>" % (self.__class__.__name__,self.username)

# marshmallow转换数据格式主要通过架构转换类来完成.
# 在marshmallow使用过程中所有的架构转换类必须直接或间接继承于Schema基类
# Schema类提供了数据转换的最基本功能:序列化,验证和反序列化
from marshmallow import Schema,fields
class UserSchema(Schema):
    username = fields.String()
    mobile   = fields.String()
    sex      = fields.Boolean()
    email    = fields.Email()
    created_time = fields.DateTime()
    updated_time = fields.DateTime()

@app.route("/")
def index():
    user = User(
        username="xiaoming",
        mobile="13312345677",
        sex=True,
        email="133123456@qq.com",
        created_time=datetime.now(),
        updated_time=datetime.now()
    )
    us = UserSchema() # 序列化多个数据,可以使用many=True
    ret = us.dump(user)# 格式化输出成字典
    ret = us.dumps(user)# 格式化输出成json字符串
    """打印格式:
    {"created_time": "2020-11-30T23:17:46.650550", "updated_time": "2020-11-30T23:17:46.650559", "sex": true, "mobile": "13312345677", "username": "xiaoming", "email": "133123456@qq.com"}
    """
    return "ok"

if __name__ == '__main__':
    with app.app_context():
        db.create_all()
    app.run(debug=True,port=6066)

schema常用属性数据类型

类型 描述
fields.Dict(keys, type]] = None, values, …) 字典类型,常用于接收json类型数据
fields.List(cls_or_instance, type], **kwargs) 列表类型,常用于接收数组数据
fields.Tuple(tuple_fields, *args, **kwargs) 元组类型
fields.String(*, default, missing, data_key, …) 字符串类型
fields.UUID(*, default, missing, data_key, …) UUID格式类型的字符串
fields.Number(*, as_string, **kwargs) 数值基本类型
fields.Integer(*, strict, **kwargs) 整型
fields.Decimal(places, rounding, *, allow_nan, …) 数值型
fields.Boolean(*, truthy, falsy, **kwargs) 布尔型
fields.Float(*, allow_nan, as_string, **kwargs) 浮点数类型
fields.DateTime(format, **kwargs) 日期时间类型
fields.Time(format, **kwargs) 时间类型
fields.Date(format, **kwargs) 日期类型
fields.Url(*, relative, schemes, Set[str]]] = None, …) url网址字符串类型
fields.Email(*args, **kwargs) 邮箱字符串类型
fields.IP(*args[, exploded]) IP地址字符串类型
fields.IPv4(*args[, exploded]) IPv4地址字符串类型
fields.IPv6(*args[, exploded]) IPv6地址字符串类型
fields.Method(serialize, deserialize, **kwargs) 基于Schema类方法返回值的字段
fields.Function(serialize, Any], Callable[[Any, …) 基于函数返回值得字段
fields.Nested(nested, type, str, Callable[[], …) 外键类型

Schema数据类型的常用通用属性

属性名 描述
default 序列化阶段中设置字段的默认值
missing 反序列化阶段中设置字段的默认值
validate 反序列化阶段调用的内置数据验证器或者内置验证集合
required 设置当前字段的必填字段
allow_none 是否允许为空
load_only 是否在反序列化阶段才使用到当前字段 ,相当于write_only
dump_omly 是否在序列化阶段才使用到当前字段, 相当于read_only
error_messages 字典类型,可以用来替代默认的字段异常提示语,格式:
error_messages=

构造器嵌套使用

from marshmallow import Schema, fields, ValidationError, validates_schema, validates, validate

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


class Blog(object):
    def __init__(self, title, author):
        self.title = title
        self.author = author # 用来代替MySQL中的外键关系

class BlogSchema(Schema):
    title = fields.String()
    # 使用lambda匿名函数,使只有在使用author时才会找UserSchema对象,不然可能出现先后调用,导致对象找不到的情况出现
    # 用来序列化外键对象,可以将外键对象按照指定的格式进行序列化,并且外键指定的格式可以完成任何支持的marshmallow操作
    author = fields.Nested(lambda: UserSchema()) 

class UseUserSchema(Schema):
    name = fields.String()
    email = fields.String()

    
class UserSchema(Schema):
    name = fields.String()
    email = fields.Email()
    created_at = fields.DateTime(format='%Y-%m-%d %H:%H:%S')
    # 双向嵌套,例如找出所有属于该用户的所有书籍。注意:在大数据量时,数据返回将非常慢
    books = fields.List(fields.Nested(BlogSchema(exclude=("author",))))
    # friends = fields.Nested(UseUserSchema(many=True)) # 方式一:使用一个外部的图式,可以指点序列化哪些字段
    friends = fields.Nested("self",only=("name", ), many=True) # 方式二:使用自身图式作为外键的方式,并指定序列化的字段
    # friends = fields.Nested(lambda: UserSchema(many=True, only=("name",)), dump_only=True) # 方式二的一种:使用自身图式作为外键的图式,并指定序列化的字段
    # friends = fields.Pluck("self", "name", many=True) # 方式三:使用Pluck字段可以用单个值来替换嵌套的数据。
    
# 视图代码:
    try:
        user0 = User(name="南派三叔", email="sanshu@163.com")
        user1 = User(name="刘慈欣", email="sanshu@163.com")
        user2 = User(name="天下霸唱", email="sanshu@163.com")
        blog = Blog(title="盗墓笔记", author=user0)
        res = BlogSchema().dump(blog)
        print(res, type(res))
        user0.books = [blog]
        user0.friends.append(user1)
        user0.friends.append(user2)
        res = UserSchema().dump(user0)
        print(res, type(res))
    except Exception as err:
        print(err)

基于Schema完成数据反序列化转换

代码:

from marshmallow import Schema, fields, validate, ValidationError
class 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))

@app.route("/")
def index():
	user_data = {"name": "Ronnie", "email": "ronnie@stones.com"}
	schema = UserSchema()
	result = schema.load(user_data)
	print(result)  # => <User: 'Ronnie'>
    return "ok"

反序列化时转换/忽略部分数据

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

@app.route("/")
def index():
	result = UserSchema().load({"age": 42}, partial=("name",))
	print(result)  # => {'age': 42}
    return "ok"

设置字段只在序列化或反序列化阶段才启用

class UserSchema(Schema):
    name = fields.Str()
    # password is 
    password = fields.Str(load_only=True) # 相当于只写字段 "write-only"
    created_time = fields.DateTime(dump_only=True) # 相当于只读字段 "read-only"

反序列化阶段的钩子方法

post_dump([fn,pass_many,pass_original]) 注册要在序列化对象后调用的方法,它会在对象序列化后被调用。
post_load([fn,pass_many,pass_original]) 注册反序列化对象后要调用的方法,它会在验证数据之后被调用。
pre_dump([fn,pass_many]) 注册要在序列化对象之前调用的方法,它会在序列化对象之前被调用。
pre_load([fn,pass_many]) 在反序列化对象之前,注册要调用的方法,它会在验证数据之前调用。

class UserSchema(Schema):
    name = fields.String()
    sex = fields.String()
    age = fields.Integer()
    email = fields.Email()
    password = fields.Str(load_only=True)
    created_time = fields.DateTime(format='%Y-%m-%d %H:%M:%S', dump_only=True)

    @post_loada
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    # test 时间管控
    
    
    
    
    
    
    
    
    
    
    
    
    
    

反序列化阶段对数据进行验证

基于内置验证器进行数据验证

内置验证器 描述
validate.Email(*, error) 邮箱验证
validate.Equal(comparable, *, error) 判断值是否相等
validate.Length(min, max, *, equal, error) 值长度/大小验证
validate.OneOf(choices, labels, *, error) 选项验证
validate.Range([min, max]) 范围验证
validate.Regexp(regex, bytes, Pattern][, flags]) 正则验证
validate.URL(*, relative, schemes, Set[str]]] = None, …) 验证是否为URL

代码:

from marshmallow import Schema, fields, validate, ValidationError
class 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))

@app.route("/")
def index():
	data = {"name": "", "permission": "hello", "age": 71}
	try:
    	UserSchema().load(data)
	except ValidationError as err:
    	pprint(err.messages)

自定义验证方法

class ItemSchema(Schema):
    age = fields.Integer()

    @validates_schema
    def validate(self,data):
        """对所有数据进行验证"""
    	return data 
    
    @validates("age")
    def validate_age(self, data):
        """对单个数据进行验证"""
        if data < 10:
            raise ValidationError("年龄必须大于10岁")
        if data > 70:
            raise ValidationError("年龄必须小于70岁")

模型构造器(ModelSchema)

官方文档:https://github.com/marshmallow-code/marshmallow-sqlalchemy

https://marshmallow-sqlalchemy.readthedocs.io/en/latest/

注意:flask_marshmallo在0.12.0版本以后已经移除了ModelSchema和TableSchema这两个模型构造器类,官方转而推荐了使用SQLAlchemyAutoSchema和SQLAlchemySchema这2个类,前后两者用法类似。

创建

class UserSchema(SQLAlchemyAutoSchema):
    class Meta:
        model = 模型类名    # table = models.Album.__table__
        include_relationships = True  # 输出模型对象时同时对外键,是否也一并进行处理
        include_fk = True # 序序列阶段是否也一并返回主键
        load_instance = True  # 反序列化阶段时,直接返回模型对象
        sql_session = db.session # 数据库连接会话对象
        # fields= ["id","name"] # 启动的字段列表
        exclude = ["id","name"] # 排除字段列表
posted @ 2021-03-12 12:02  死里学  阅读(678)  评论(0)    收藏  举报