Marshmallow (Flask-Marshmallow) 使用指南

Marshmallow (Flask-Marshmallow) 使用指南

本文档总结了本项目中使用 flask-marshmallow (简称 ma) 进行对象序列化(Serialization)和反序列化(Deserialization)的规范与技巧。


1. 注册与初始化 (Registration)

在本项目中,ma 对象在 app/extensions.py 中被定义,并在 app/__init__.py 中通过 init_app 模式初始化。

app/extensions.py:

from flask_marshmallow import Marshmallow
ma = Marshmallow()
# ...其他扩展

app/init.py:

from app.extensions import ma
def create_app(config_name):
# ...
ma.init_app(app)
# ...

2. 与 SQLAlchemy 集成 (Model Integration)

我们通常使用 ma.SQLAlchemyAutoSchema 来自动生成基于 SQLAlchemy 模型的 Schema。这能极大减少重复代码。

基础结构

from app.extensions import ma, db
from .models import User
class UserSchema(ma.SQLAlchemyAutoSchema):
class Meta:
model = User # 绑定的 SQLAlchemy 模型
load_instance = True # load() 时自动反序列化为 Model 实例
sqla_session = db.session # 必须指定 session 才能 load

3. 字段控制 (Fields Control) - 核心内容

这是最容易混淆的部分。Marshmallow 提供了两种主要模式来控制输出字段,特别是处理 下划线命名 (snake_case)驼峰命名 (camelCase) 的转换。

模式 A:自动模式 + 局部重写 (Implicit Mode)

适用场景:希望输出 Model 的所有字段,且只针对部分字段改名或格式化。

特点

  • 不写 fields = (...)
  • Marshmallow 默认会包含 Model 的所有字段。
  • 使用 ma.auto_field(data_key='...') 来重写特定字段的输出键名。

示例

class RoleSchema(ma.SQLAlchemyAutoSchema):
class Meta:
model = Roles
load_instance = True
# 不需要写 fields,默认全包含
# 1. 重写 role_name 字段,输出为 roleName
role_name = ma.auto_field(data_key='roleName')
# 2. 没写的字段(如 id, note)会自动原样输出

模式 B:显式白名单模式 (Explicit Mode)

适用场景:字段名和 Model 不一致,或者需要严格控制输出字段(例如隐藏密码)。

特点

  • 必须写 fields = ('field1', 'field2', ...)
  • 只有在 fields 列表里的字段才会被输出。
  • 注意:如果通过 auto_field 重写了字段名,fields 里要写Schema 中定义的变量名

示例 (UserSchema)

class UserSchema(ma.SQLAlchemyAutoSchema):
class Meta:
model = Users
# 必须写 fields,否则可能会出现重复字段(如 user_id 和 userId 同时出现)
fields = ('userId', 'inDate', 'enable')
# 1. 变量名 userId (新) vs Model 属性 user_id (旧)
# 因为名字不一致,Marshmallow 不会自动关联,必须明确指定 attribute='user_id' 或第一个参数
userId = ma.auto_field('user_id', data_key='userId')
# 2. 格式化日期
inDate = ma.Function(lambda obj: obj.in_date.strftime('%Y-%m-%d'))
# 3. 自定义逻辑
enable = ma.Function(lambda obj: 1 if obj.enable == '1' else 0)

模式转化对比

目标:把数据库的 user_name 输出为前端的 userName

模式写法优点缺点
自动模式user_name = ma.auto_field(data_key='userName')
(Meta 中不写 fields)
简单,自动包含其他字段变量名必须是 user_name (下划线),不符合某些 Python 纯驼峰代码风格
显式模式userName = ma.auto_field('user_name', data_key='userName')
(Meta 中写 fields=('userName', ...))
变量名可以是 userName,符合驼峰风格比较繁琐,必须手动维护 fields 列表,否则字段会丢或重

本项目推荐

  • 如果只是简单改名,推荐 自动模式(变量名保持下划线,用 data_key 改输出名)。
  • 如果涉及复杂逻辑或安全性(如 UserSchema),推荐 显式模式

4. Meta 选项详解

class Meta 中,常用的配置项如下:

选项说明示例值
model绑定的 SQLAlchemy 模型类Users
load_instance反序列化时是否创建模型实例True (推荐), False (只返回字典)
sqla_session数据库会话,用于查询和反序列化db.session
fields白名单:只允许这些字段输出('id', 'name', 'createTime')
exclude黑名单:排除这些字段('password', 'secret_key')
dateformat默认日期格式字符串'%Y-%m-%d'
datetimeformat默认时间格式字符串'%Y-%m-%d %H:%M:%S'
unknown处理未知字段的行为EXCLUDE (忽略), INCLUDE, RAISE (报错)

5. 常用方法与使用 (Usage)

初始化 Schema

# 处理单个对象
user_schema = UserSchema()
# 处理对象列表 (List)
users_schema = UserSchema(many=True)

序列化 (Serialization)

Model -> Dict/JSON

user = Users.query.first()
# Dump 单个
result = user_schema.dump(user)
# result: {'userId': 1, 'userName': 'admin'}
# Dump 列表
users = Users.query.all()
results = users_schema.dump(users)
# results: [{'userId': 1...}, {'userId': 2...}]

反序列化 (Deserialization)

Dict/JSON -> Model

data = {'userName': 'new_user', 'password': '123'}
try:
# Load 会自动进行验证 (Validate) 和转换
# 如果 Meta 中 load_instance=True,这就返回一个 Users 对象
new_user = user_schema.load(data)
db.session.add(new_user)
db.session.commit()
except ma.ValidationError as err:
print(err.messages) # 验证失败信息

仅验证 (Validation Only)

如果不希望创建对象,只想检查数据格式:

errors = user_schema.validate(data)
if errors:
print("数据校验失败:", errors)