Marshmallow
一、模块简介
Marshmallow,中文译作:棉花糖。是一个轻量级的数据格式转换的模块,也叫序列化和反序列化模块,常用于将复杂的orm模型对象与python原生数据类型之间相互转换。marshmallow提供了丰富的api功能。如下:
Serializing
序列化[可以把数据模型对象转化为可存储或可传输的数据类型,例如:objects/object->list/dict,dict/list->string]
Deserializing
反序列化器[把可存储或可传输的数据类型转换成数据模型对象,例如:list/dict->objects/object,string->dict/list]
Validation
数据校验,可以在反序列化阶段,针对要转换数据的内容进行类型验证或自定义验证。
模块安装:
pip install -U marshmallow-sqlalchemy pip install -U flask-sqlalchemy -i https://pipy.douban.com/simple pip install -U flask-marshmallow
docs/marshDemo.py,模块初始化:
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/mofang?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)
marshmallow转换数据格式主要通过构造器类来完成。在marshmallow使用过程中所有的构造器类必须直接或间接继承于Schema基类,而Schema类提供了数据转换的基本功能:序列化,验证和反序列化
from datetime import datetime from flask import Flask from flask_sqlalchemy import SQLAlchemy from flask_marshmallow import Marshmallow 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() db.init_app(app) ma = Marshmallow() 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) # marshmallow转换数据格式主要通过架构转换类来完成. # 在marshmallow使用过程中所有的架构转换类必须直接或间接继承于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() ) # print(user) # 调用marsh把模型转换成python基本数据格式[字典/列表] us = UserSchema() # 序列化多个数据,可以使用many=True ret1 = us.dump(user) # 格式化输出成字典 ret2 = us.dumps(user) # 格式化输出成json字符串 print(">>>> us.dump(user) --> 字典") print(ret1) print(">>>> us.dumps(user) --> json字符串") print(ret2) """运行结果: >>>> us.dump(user) --> 字典 {'created_time': '2021-03-02T11:07:45.520209', 'updated_time': '2021-03-02T11:07:45.520221', 'username': 'xiaoming', 'email': '133123456@qq.com', 'sex': True, 'mobile': '13312345677'} >>>> us.dumps(user) --> json字符串 {"created_time": "2021-03-02T11:07:45.520209", "updated_time": "2021-03-02T11:07:45.520221", "username": "xiaoming", "email": "133123456@qq.com", "sex": true, "mobile": "13312345677"} """ print(type(ret1), type(ret2)) user1 = User( username="xiaoming1号", mobile="13312345677", sex=True, email="133123456@qq.com", created_time=datetime.now(), updated_time=datetime.now() ) user2 = User( username="xiaoming2号", mobile="13312345677", sex=True, email="133123456@qq.com", created_time=datetime.now(), updated_time=datetime.now() ) user_list = [user,user1,user2] us = UserSchema() data_list = us.dump(user_list,many=True) print(data_list) """运行结果: [{'mobile': '13312345677', 'created_time': '2021-03-02T11:12:50.128294', 'email': '133123456@qq.com', 'username': 'xiaoming'}, {'mobile': '13312345677', 'created_time': '2021-03-02T11:12:50.129576', 'email': '133123456@qq.com', 'username': 'xiaoming1号'}, {'mobile': '13312345677', 'created_time': '2021-03-02T11:12:50.129642', 'email': '133123456@qq.com', 'username': 'xiaoming2号'}] """ return "基本使用:模型序列化" if __name__ == '__main__': with app.app_context(): db.create_all() app.run(debug=True,host="0.0.0.0",port=5999)
4.1 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[[], …) |
外键类型 |
4.2 Schema数据类型的常用通用属性
| 描述 | |
|---|---|
| default | 序列化阶段中设置字段的默认值 |
| missing | 反序列化阶段中设置字段的默认值 |
| validate | 反序列化阶段调用的内置数据验证器或者内置验证集合 |
| required | 设置当前字段的必填字段 |
| allow_none | 是否允许为空 None,"" |
| load_only | 是否在反序列化阶段才使用到当前字段,相当于之前的write_only |
| dump_only | 是否在序列化阶段才使用到当前字段,相当于之前的read_only |
| error_messages | 字典类型,可以用来替代默认的字段异常提示语,格式: error_messages={“required”: “用户名为必填项。”} |
from flask import Flask from flask_sqlalchemy import SQLAlchemy from flask_marshmallow import Marshmallow from marshmallow import Schema,fields from datetime import datetime app = Flask(__name__) app.config["SQLALCHEMY_DATABASE_URI"]="mysql://root:123@127.0.0.1:3306/mofang?charset=utf8mb4" app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False db = SQLAlchemy() ma = Marshmallow() db.init_app(app) ma.init_app(app) # 仿模型的一个类 class Author(object): def __init__(self, name, email): self.name = name self.email = email self.created_time = datetime.now() self.books = [] # 1对多的外键 self.courses = [] # 多对多的外键 class Book(object): def __init__(self, title, author): self.title = title self.author = author # 用来代替MySQL中的外键关系 class Course(object): """写作课程""" def __init__(self,name): self.name = name self.students = [] class Book2Schema(Schema): title = fields.String() class AuthorSchema(Schema): name = fields.String() email = fields.String() # 双向嵌套,例如找出所有属于该作者的所有书籍。注意:在大数据量时,数据返回将非常慢 # books = fields.List(fields.Nested(lambda: BookSchema(exclude=["author"]))) books = fields.List(fields.Nested(lambda: Book2Schema())) class BookSchema(Schema): title = fields.String() # 使用lambda匿名函数,使只有在使用author时才会找AuthorSchema对象,不然可能出现先后调用,导致对象找不到的情况出现 # 用来序列化外键对象,可以将外键对象按照指定的格式进行序列化,并且外键指定的格式可以完成任何支持的marshmallow操作 author = fields.Nested(lambda: AuthorSchema()) class CourseSchema(Schema): name = fields.Str() # 等同于 fields.String() students = fields.List(fields.Nested(lambda: Author3Schema(exclude=["courses"]))) class Author3Schema(Schema): name = fields.String() email = fields.String() # 方式一:通用嵌套 # courses = fields.List(fields.Nested(lambda: CourseSchema(exclude=["students"]))) # 方式二:使用自身构造器作为外键的方式,并指定序列化的字段 # courses = fields.Nested(CourseSchema, only=("name",), many=True) # 方式三:也可以在方式二的基础上,不指定字段 # courses = fields.Nested(CourseSchema(many=True)) # 方式四:使用Pluck字段可以用单个值来替换嵌套的数据,返回列表数据,只有name作为成员,这里的name就是外键模型中的字段,也可以是别的字段名,name仅仅是举例 courses = fields.Pluck(CourseSchema, "name", many=True) @app.route("/index1") def index1(): """多对1的嵌套序列化""" author = Author( name="小明", email="xiaomnig@qq.com", ) book = Book( title = "小明历险记", author = author, ) bs = BookSchema() book_dict1 = bs.dump(book) book_dict2 = bs.dumps(book) print(book_dict1) print(book_dict2) return "基本使用:嵌套序列化" @app.route("/index2") def index2(): """1对多的嵌套序列化""" author = Author( name="小明", email="xiaomnig@qq.com" ) author.books = [ Book( title="小明历险记第1部", author=author, ),Book( title="小明历险记第2部", author=author, ),Book( title="小明历险记第3部", author=author, ),Book( title="小明历险记第4部", author=author, ), ] aus = AuthorSchema() ret1 = aus.dump(author) ret2 = aus.dumps(author) print(ret1) print(ret2) return "基本使用:嵌套序列化" @app.route("/index3") def index3(): """多对多的嵌套序列化""" author1 = Author( name="xiaoming", email="xiaomnig@qq.com" ) course1 = Course(name="散文小说") author1.courses = [ course1, Course(name="言情小说"), Course(name="武侠小说"), ] course1.students = [ author1, Author( name="xiaoli", email="xiaoli@qq.com" ) ] aus = Author3Schema() ret1 = aus.dump(author1) print(ret1) cs = CourseSchema() ret2 = cs.dump(course1) print(ret2) return "基本使用:嵌套序列化" if __name__ == '__main__': app.run(debug=True,port=7000, host="0.0.0.0")
4.4 自关联
自关联:一张数据表中存在主外键的情况,就是自关联。
当前,我们要在构造器/序列化器中完成这种子关联的序列化,我们可以称之为自嵌套序列化:
| name | parent/pid | |
|---|---|---|
| 1 | 河北省 | 0 |
| 2 | 广东省 | 0 |
| 3 | 石家庄 | 1 |
| 4 | 深圳市 | 2 |
| 5 | 南山区 | 4 |
| 6 | 裕华区 | 3 |
把上面的表可以拆分成2张或3张虚拟的表
| name | ||
|---|---|---|
| 1 | 河北省 | |
| 2 | 广东省 |
| name | parent/pid | |
|---|---|---|
| 3 | 石家庄 | 1 |
| 4 | 深圳市 | 2 |
| 5 | 南山区 | 4 |
| 6 | 裕华区 | 3 |
代码:
from flask import Flask from flask_sqlalchemy import SQLAlchemy from flask_marshmallow import Marshmallow from marshmallow import Schema,fields from datetime import datetime app = Flask(__name__) app.config["SQLALCHEMY_DATABASE_URI"]="mysql://root:123@127.0.0.1:3306/mofang?charset=utf8mb4" app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False db = SQLAlchemy() ma = Marshmallow() db.init_app(app) ma.init_app(app) class Area(object): def __init__(self, id, name): self.id = id self.name = name self.sub = [] class AreaSchema(Schema): id = fields.Integer() name = fields.String() # 方式一:通用嵌套 # sub = fields.List(fields.Nested(lambda: AreaSchema(exclude=["sub"]))) # 方式二:使用自身构造器作为外键的方式,并指定序列化的字段 # sub = fields.Nested("self", only=("id","name",), many=True) # 方式三:也可以在方式二的基础上,不指定字段 # sub = fields.Nested(lambda: AreaSchema(many=True)) # 方式四:使用Pluck字段可以用单个值来替换嵌套的数据,返回列表数据,只有name作为成员,这里的name就是外键模型中的字段,也可以是别的字段名,name仅仅是举例 sub = fields.Pluck("self", "name", many=True,) @app.route("/") def index(): """自嵌套序列化""" area1 = Area( id=1, name="广东省" ) area2 = Area( id=2, name="广州市" ) area3 = Area( id=3, name="白云区" ) area4 = Area( id=4, name="荔湾区" ) area5 = Area( id=5, name="河北省" ) area6 = Area( id=6, name="石家庄市" ) area7 = Area( id=7, name="桥东区" ) area8 = Area( id=8, name="桥西区" ) area1.sub = [area2] area2.sub = [area3,area4] area5.sub = [area6] area6.sub = [area7,area8] ars = AreaSchema() ret1 = ars.dump(area1) ret2 = ars.dump(area2) print(ret1) print(ret2) return "基本使用:自嵌套序列化" if __name__ == '__main__': app.run(debug=True,port=7000, host="0.0.0.0")
五 、
代码:
from flask import Flask from flask_sqlalchemy import SQLAlchemy from flask_marshmallow import Marshmallow from marshmallow import Schema,fields,validate from datetime import datetime app = Flask(__name__) app.config["SQLALCHEMY_DATABASE_URI"]="mysql://root:123@127.0.0.1:3306/mofang?charset=utf8mb4" app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False db = SQLAlchemy() ma = Marshmallow() db.init_app(app) ma.init_app(app) class UserSchema(Schema): name = fields.Str(validate=validate.Length(min=1)) email = fields.Email() permission = fields.Str(validate=validate.OneOf(["read", "write", "admin"])) age = fields.Int(validate=validate.Range(min=18, max=40)) @app.route("/") def index(): us = UserSchema() user_data = {"email": "ronnie@stones.com","permission":"admin"} ret1 = us.load(user_data) print(ret1) return "基本使用:模型序反列化" if __name__ == '__main__': app.run(debug=True,port=7000, host="0.0.0.0")
from flask import Flask from flask_sqlalchemy import SQLAlchemy from flask_marshmallow import Marshmallow from marshmallow import Schema,fields,validate from datetime import datetime app = Flask(__name__) app.config["SQLALCHEMY_DATABASE_URI"]="mysql://root:123@127.0.0.1:3306/mofang?charset=utf8mb4" app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False db = SQLAlchemy() ma = Marshmallow() db.init_app(app) ma.init_app(app) class UserSchema(Schema): name = fields.Str(required=True, validate=validate.Length(min=1)) email = fields.Email() permission = fields.Str(validate=validate.OneOf(["read", "write", "admin"])) age = fields.Int(validate=validate.Range(min=18, max=40)) @app.route("/") def index(): us = UserSchema() user_data = {"email": "ronnie@stones.com","permission":"admin"} ret1 = us.load(user_data,partial=("name",)) print(ret1) return "基本使用:模型序反列化" if __name__ == '__main__': app.run(debug=True,port=7000, host="0.0.0.0")
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"
代码:
from flask import Flask from flask_sqlalchemy import SQLAlchemy from flask_marshmallow import Marshmallow from marshmallow import Schema,fields,validate from datetime import datetime app = Flask(__name__) app.config["SQLALCHEMY_DATABASE_URI"]="mysql://root:123@127.0.0.1:3306/mofang?charset=utf8mb4" app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False db = SQLAlchemy() ma = Marshmallow() db.init_app(app) ma.init_app(app) class User(object): def __init__(self,name,email,age,password,permission): self.name=name self.email=email self.age=age self.password=password self.permission=permission self.created_time = datetime.now() class UserSchema(Schema): name = fields.Str(required=True, validate=validate.Length(min=1)) email = fields.Email() age = fields.Int(validate=validate.Range(min=18, max=40)) password = fields.Str(required=True, load_only=True) permission = fields.Str(validate=validate.OneOf(["read", "write", "admin"])) created_time = fields.DateTime(dump_only=True) @app.route("/") def index(): us = UserSchema() user_data = {"email": "ronnie@stones.com","permission":"admin","password":"123456"} ret1 = us.load(user_data,partial=("name",)) print(ret1) user = User( name="xiaoming", age=18, **user_data, ) ret2 = us.dump(user) print(ret2) return "基本使用:模型序反列化" if __name__ == '__main__': app.run(debug=True,port=7000, host="0.0.0.0")
marshmallow一共提供了4个钩子方法:
pre_dump([fn,pass_many]) 注册要在序列化对象之前调用的方法,它会在序列化对象之前被调用。
pre_load([fn,pass_many]) 在反序列化对象之前,注册要调用的方法,它会在验证数据之前调用。
post_dump([fn,pass_many,pass_original]) 注册要在序列化对象后调用的方法,它会在对象序列化后被调用。
post_load([fn,pass_many,pass_original]) 注册反序列化对象后要调用的方法,它会在验证数据之后被调用。
from flask import Flask from flask_sqlalchemy import SQLAlchemy from flask_marshmallow import Marshmallow from werkzeug.security import generate_password_hash from marshmallow import Schema,fields,validate,pre_load,pre_dump,post_dump,post_load from datetime import datetime app = Flask(__name__) app.config["SQLALCHEMY_DATABASE_URI"]="mysql://root:123@127.0.0.1:3306/mofang?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") name = db.Column(db.String(255), index=True, comment="用户名") age = db.Column(db.Integer, comment="年龄") password = db.Column(db.String(255), comment="登录密码") mobile = db.Column(db.String(20), comment="手机号") created_time = db.Column(db.DateTime, default=datetime.now, comment="创建时间") def __repr__(self): return "<%s: %s>" % (self.__class__.__name__, self.name) class UserSchema(Schema): name = fields.String(validate=validate.Length(min=1)) age = fields.Integer(required=True) password = fields.Str(load_only=True) # 相当于只写字段 "write-only" # created_time = fields.DateTime(dump_only=True) # 相当于只读字段 "read-only" mobile = fields.String() created_time = fields.DateTime(format='%Y-%m-%d %H:%M:%S') @pre_load def pre_load(self,data,**kwargs): """反序列化的前置钩子,会在数据验证之前执行""" # print(f"kwargs={kwargs}") data["created_time"] = datetime.now().strftime('%Y-%m-%d %H:%M:%S') # 必须返回数据 return data @post_load def post_load(self,data,**kwargs): """反序列化的后置钩子,会在数据验证之后执行""" # print(f"kwargs={kwargs}") # 在进入数据库之前,密码加密 data["password"] = generate_password_hash(data["password"]) # 在进入数据库之前,删除不必要的字段,例如验证码,确认密码 return User(**data) # 也可以在此手动把字典转换成模型对象 # 注意,此处没有提交数据库,所以这里仅仅是实例化了一个模型对象,并没有完成添加数据库操作 # 如果要在反序列化后同步到数据库,则代码如下: # instance = User(**data) # db.session.add(instance) # db.session.commit() # return instance @pre_dump def pre_dump(self,data,**kwargs): """序列化的前置钩子,会在数据转换之前执行""" data.mobile = "130____0001" return data @post_dump def post_dump(self,data,**kwargs): """序列化的后置钩子,会在数据转换之后执行""" data["age"] = f"{data['age']}岁" return data @app.route("/") def index(): us = UserSchema() instance = us.load({"name":"xiaomingf","age":19,"password":"123456","mobile":"13000000001"}) data = us.dump(instance) print(data) return "基本使用:钩子方法的调用" if __name__ == '__main__': app.run(debug=True,port=7000, host="0.0.0.0")
| 描述 | |
|---|---|
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 datetime import datetime from flask import Flask from flask_marshmallow import Marshmallow from marshmallow import Schema,fields,validate,post_dump,post_load,pre_dump,pre_load from flask_sqlalchemy import SQLAlchemy from werkzeug.security import generate_password_hash 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() db.init_app(app) ma = Marshmallow() ma.init_app(app) class User(db.Model): __tablename__ = "tb_user" id = db.Column(db.Integer, primary_key=True, comment="主键ID") name = db.Column(db.String(255), index=True, comment="用户名") email = db.String(db.String(255)) age = db.Column(db.Integer, comment="年龄") password = db.Column(db.String(255), comment="登录密码") mobile = db.Column(db.String(20), comment="手机号") def __repr__(self): return "<%s: %s>" % (self.__class__.__name__, self.name) class UserSchema(Schema): name = fields.String(validate=validate.Length(min=1)) age = fields.Integer(required=True,validate=validate.Range(min=16,max=100)) email = fields.String(validate=validate.Email()) password = fields.Str(load_only=True,validate=validate.Length(min=6,max=16)) # 相当于只写字段 "write-only" mobile = fields.String(validate=validate.Regexp("^1[3-9]\d{9}$",error="手机号格式不正确!")) @app.route("/") def index(): user_data = {"email":"xiaoming@qq.com","name": "小明", "age": 20,"password":"123456","mobile":"13312345678"} # 报错 us = UserSchema() # 反序列化 instance = us.load(user_data) print(instance) return "hello" if __name__ == '__main__': app.run(debug=True,host="0.0.0.0",port=5999)
通过context或者构造器实例对象进行参数传递
import random from datetime import datetime from flask import Flask from flask_marshmallow import Marshmallow from marshmallow import Schema,fields,validate,validates,validates_schema,ValidationError,post_load,pre_load from flask_sqlalchemy import SQLAlchemy 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() db.init_app(app) ma = Marshmallow() ma.init_app(app) class User(db.Model): __tablename__ = "tb_user" id = db.Column(db.Integer, primary_key=True, comment="主键ID") name = db.Column(db.String(255), index=True, comment="用户名") email = db.String(db.String(255)) age = db.Column(db.Integer, comment="年龄") password = db.Column(db.String(255), comment="登录密码") mobile = db.Column(db.String(20), comment="手机号") def __repr__(self): return "<%s: %s>" % (self.__class__.__name__, self.name) class UserSchema(Schema): name = fields.String() age = fields.Integer() email = fields.String() password = fields.Str() # 密码 password2 = fields.String() # 确认密码 mobile = fields.String(validate=validate.Regexp("^1[3-9]\d{9}$",error="手机号格式不正确!")) # 针对单个指定字段的值进行验证 @validates("mobile") def validate_mobile(self,mobile): if(mobile == "13312345678"): raise ValidationError("手机号已经被注册!!") return mobile # 针对多个字段的验证 @validates_schema def validate(self,data,**kwargs): if(data["password"] != data["password2"]): # 注意:验证失败以后,一定是raise抛出异常!!!不能是return!!!! raise ValidationError(field_name="password2",message="密码和确认密码不一致!") return data @post_load def post_load(self,data,**kwargs): """反序列化验证后的钩子方法""" print("num=%s" % self.num) print(self.context) del data["password2"] # 删除掉不必要的字段 return User(**data) @app.route("/") def index(): user_data = {"email":"xiaoming@qq.com","name": "小明", "age": 20,"password":"123456","mobile":"13312345671","password2": "123456"} num = random.randint(1,100) # 如果将来在开发中有部分数据需要传递到构造器中进行调用,可以在实例化构造器时通过context传递参数进行,在构造器内部通过self.context调用 us = UserSchema(context={"num":num}) # 如果将来在开发中有部分数据需要传递到构造器中进行调用,可以作为构造器对象的属性进行传递 us.num = num # 反序列化 instance = us.load(user_data) print(instance) return "hello" if __name__ == '__main__': app.run(debug=True,host="0.0.0.0",port=5999)
https://marshmallow-sqlalchemy.readthedocs.io/en/latest/
注意:flask_marshmallow在0.12.0版本以后已经移除了ModelSchema和TableSchema这两个模型构造器类,官方转而推荐了使用SQLAlchemyAutoSchema和SQLAlchemySchema这2个类,前后两者用法类似。
代码:
from flask import Flask from flask_sqlalchemy import SQLAlchemy from flask_marshmallow import Marshmallow app = Flask(__name__) app.config["SQLALCHEMY_DATABASE_URI"]="mysql://root:123@127.0.0.1:3306/mofang?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") name = db.Column(db.String(255), nullable=False, index=True, comment="用户名") email = db.Column(db.String(255)) age = db.Column(db.Integer, comment="年龄") password = db.Column(db.String(255), comment="登录密码") mobile = db.Column(db.String(20), comment="手机号") def __repr__(self): return "<%s: %s>" % (self.__class__.__name__, self.name) from marshmallow_sqlalchemy import SQLAlchemySchema,auto_field from marshmallow import post_load,fields,validate class UserSchema(SQLAlchemySchema): """模型构造器""" # 主键会被自动设置为默认自读[dump_only] id = auto_field() # auto_field 一般可以称之为 继承字段,它会自动到对应的模型中继承同名的字段声明和约束 name = auto_field() email = auto_field() age = auto_field() password = auto_field(validate=validate.Length(min=6,max=16)) # # auto_field在使用过程中,除了可以复制模型对应字段的信息和数据类型以外,我们也可以增加补充说明 mobile = auto_field(required=True,validate=validate.Regexp("^1[3-9]\d{9}$")) password2 = fields.String() # 如果模型中没有声明的字段,则还是按照之前自定义构造器的方式自己填写 class Meta: model = User # 模型类名 table = models.Album.__table__ load_instance = True # 反序列化阶段时,True会直接返回模型对象,False会返回字典 include_relationships = True # 输出模型对象时同时对外键,是否也一并进行处理 include_fk = True # 序列化阶段是否也一并返回主键,也就是是否返回ID @post_load def post_load(self,instance,**kwargs): """反序列化之后执行的钩子""" db.session.add(instance) db.session.commit() return instance @app.route("/") def index(): user_data = {"name": "小明1", "mobile": "13312345676","age":18,"email":"133@qq.com","password":"123456","password2":"123456"} us = UserSchema(session=db.session) instance = us.load(user_data) # 注意:如果我们当前调用的是模型构造器,则必须传入session属性,属性是当前数据库的session会话对象 print(instance) return "基本使用:反序列化阶段使用的验证器" if __name__ == '__main__': with app.app_context(): db.create_all() app.run(debug=True,port=7000, host="0.0.0.0")
用法:
class 构造器类名(SQLAlchemyAutoSchema): class Meta: model = 模型类名 # table = models.Album.__table__ include_relationships = True # 输出模型对象时同时对外键,是否也一并进行处理 include_fk = True # 序序列阶段是否也一并返回主键 load_instance = True # 反序列化阶段时,直接返回模型对象 sqla_session = db.session # 数据库连接会话对象 # fields= ["id","name"] # 启动的字段列表 exclude = ["id","name"] # 排除字段列表
from flask import Flask from flask_sqlalchemy import SQLAlchemy from flask_marshmallow import Marshmallow app = Flask(__name__) app.config["SQLALCHEMY_DATABASE_URI"]="mysql://root:123@127.0.0.1:3306/mofang?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") name = db.Column(db.String(255), nullable=False, index=True, comment="用户名") email = db.Column(db.String(255)) age = db.Column(db.Integer, comment="年龄") password = db.Column(db.String(255), comment="登录密码") mobile = db.Column(db.String(20), comment="手机号") def __repr__(self): return "<%s: %s>" % (self.__class__.__name__, self.name) from marshmallow_sqlalchemy import SQLAlchemyAutoSchema,auto_field from marshmallow import post_load,fields,validate class UserSchema(SQLAlchemyAutoSchema): """模型构造器""" password2 = fields.String() # 如果模型中没有声明的字段,则还是按照之前自定义构造器的方式自己填写 class Meta: model = User # 模型类名 # 白名单字段,序列化器中使用的字段都要在这里声明 # fields = ["id","name","password2","email","age","mobile"] # 字段列表 # 黑名单字段,序列化器中不使用的字段,与白名单互斥,设置了该属性,则剩余字段就是全部使用的了 exclude = ["age"] # 构造器禁用的字段列表 load_instance = True # 反序列化阶段时,True会直接返回模型对象,False会返回字典 include_relationships = True # 输出模型对象时同时对外键,是否也一并进行处理 include_fk = True # 序列化阶段是否也一并返回主键,也就是是否返回ID sqla_session = db.session # 当前数据库连接的session会话对象 @post_load def post_load(self,instance,**kwargs): """反序列化之后执行的钩子""" db.session.add(instance) db.session.commit() return instance @app.route("/") def index(): user_data = {"name": "小明1", "mobile": "13312345676","email":"133@qq.com","password":"123456","password2":"123456"} us = UserSchema() instance = us.load(user_data) # 注意:如果我们当前调用的是模型构造器,则必须传入session属性,属性是当前数据库的session会话对象 print(instance.id) return "基本使用:反序列化阶段使用的验证器" if __name__ == '__main__': with app.app_context(): db.create_all() app.run(debug=True,port=7000, host="0.0.0.0")

浙公网安备 33010602011771号