如何配置marshmallow_sqlalchemy使模型序列化包含关联对象并掌握关联对象反序列化及Meta类相关配置
一、修改代码实现关联对象序列化输出
你当前的代码中,AuthorSchema默认不会自动序列化关联的books字段,再加上Author类里的books关系设置了lazy="dynamic"(返回Query对象而非列表),所以dump时不会包含书籍列表。要实现目标输出,我们可以从两方面调整:
修改后的完整代码
import sqlalchemy as sa from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import scoped_session, sessionmaker, relationship, backref engine = sa.create_engine("sqlite:///:memory:") session = scoped_session(sessionmaker(bind=engine)) Base = declarative_base() class Author(Base): __tablename__ = "authors" id = sa.Column(sa.Integer, primary_key=True) name = sa.Column(sa.String, nullable=False) # 将lazy改为"select"(默认值),让author.books直接返回书籍列表而非Query对象 books = relationship("Book", backref=backref("author", lazy="joined"), foreign_keys="Book.author_id", lazy="select") def __repr__(self): return "<Author(name={self.name!r})>".format(self=self) class Book(Base): __tablename__ = "books" id = sa.Column(sa.Integer, primary_key=True) title = sa.Column(sa.String) author_id = sa.Column(sa.Integer, sa.ForeignKey("authors.id")) Base.metadata.create_all(engine) from marshmallow_sqlalchemy import SQLAlchemySchema, auto_field, SQLAlchemyAutoSchema class BookSchema(SQLAlchemyAutoSchema): class Meta: model = Book load_instance = True include_fk = True class AuthorSchema(SQLAlchemyAutoSchema): # 显式声明books字段,auto_field会自动关联BookSchema并识别many=True books = auto_field() class Meta: model = Author load_instance = True include_fk = True author = Author(name="Chuck Paluhniuk") author_schema = AuthorSchema() book = Book(title="Fight Club", author=author) book_schema = BookSchema() session.add(author) session.add(book) session.commit() dump_data_author = author_schema.dump(author) print(dump_data_author) dump_data_book = book_schema.dump(book) print(dump_data_book)
关键调整说明
- 调整relationship的lazy属性:把
books的lazy="dynamic"改为lazy="select",这样author.books会直接返回书籍对象列表,而非需要手动执行的Query对象,方便marshmallow序列化。 - 显式添加关联字段到Schema:在
AuthorSchema中添加books = auto_field(),auto_field会自动识别这是一个一对多的关联关系,使用BookSchema来序列化每个书籍对象,并自动设置many=True。
如果不想修改lazy="dynamic",也可以用Method字段手动处理Query对象:
from marshmallow import fields class AuthorSchema(SQLAlchemyAutoSchema): books = fields.Method("get_books") def get_books(self, obj): # 执行Query并序列化结果 return BookSchema(many=True).dump(obj.books.all()) class Meta: model = Author load_instance = True include_fk = True
二、关联对象的反序列化控制方法
反序列化就是将JSON数据转换为SQLAlchemy模型实例,包括关联对象的处理,这里举几个常见场景:
1. 同时创建主对象和关联对象
比如我们有如下输入数据:
input_data = { "name": "George Orwell", "books": [ {"title": "1984"}, {"title": "Animal Farm"} ] }
要反序列化为包含书籍的Author实例,只需确保:
AuthorSchema和BookSchema的load_instance都设为True- 反序列化时传入
session参数(用于持久化关联对象)
author = author_schema.load(input_data, session=session) session.add(author) session.commit()
2. 仅关联已存在的对象
如果不想创建新的书籍,只希望关联数据库中已存在的书籍,可以限制books字段只接收id:
class AuthorSchema(SQLAlchemyAutoSchema): books = auto_field(only=["id"]) class Meta: model = Author load_instance = True include_fk = True # 输入只需传入书籍id input_data = { "name": "George Orwell", "books": [{"id": 1}, {"id": 2}] } author = author_schema.load(input_data, session=session)
3. 禁止反序列化关联字段
如果不想让客户端通过输入修改关联的书籍,可以将books设为dump_only:
class AuthorSchema(SQLAlchemyAutoSchema): books = auto_field(dump_only=True) class Meta: model = Author load_instance = True include_fk = True
三、Schema类Meta中常用配置项详解
在marshmallow_sqlalchemy的Schema类的Meta子类中,这些配置项可以帮你精细控制序列化和反序列化行为:
load_only:字段名列表,这些字段仅用于反序列化(从输入加载到模型),不会出现在序列化输出中。比如用户密码字段,只在创建/更新时接收,不会返回给客户端:class Meta: load_only = ("password",)dump_only:字段名列表,这些字段仅用于序列化(从模型输出到JSON),反序列化时会忽略输入中的这些字段。比如数据库自动生成的id、created_at:class Meta: dump_only = ("id", "created_at")exclude:字段名列表,这些字段完全不参与序列化和反序列化。比如内部备注字段不需要对外暴露:class Meta: exclude = ("internal_note",)include:字段名列表,指定只包含这些字段参与序列化和反序列化,优先级高于exclude:class Meta: include = ("id", "name")model:必填项,指定Schema对应的SQLAlchemy模型类,SQLAlchemyAutoSchema会基于这个模型自动生成字段。load_instance:布尔值,设为True时,反序列化返回模型实例;设为False时返回普通字典。默认是False。include_fk:布尔值,设为True时,会包含模型中的外键字段(比如author_id)在序列化和反序列化中,默认是False。
内容的提问来源于stack exchange,提问作者Amin Ba

