SQLAlchemy:浅拷贝避免延迟加载

5 投票
1 回答
2218 浏览
提问于 2025-04-15 15:22

我正在尝试自动创建一个SA映射对象的浅拷贝。目前我的函数就是:

newobj = src.__class__()
for prop in class_mapper(src.__class__).iterate_properties:
    setattr(newobj, prop.key, getattr(src, prop.key))

但是我在处理懒加载关系时遇到了问题……显然,使用getattr会触发懒加载,但因为我并不需要这些值,所以我想只复制这个属性的“应该懒加载”的状态……这样做可以吗?

编辑:我需要这个功能来做一个“数据记录”系统。也就是说,每当有人更新一个持久化的实体时,我需要生成一个新记录,然后把旧记录标记为已更新。
为此,我创建了这个实体的浅拷贝(这样SQLA就会执行插入而不是更新),然后从这里开始工作。这个系统运行得相当不错(已经在生产环境中使用了几个月),但现在我想增强它,使得不需要先加载所有的关系……

1 个回答

6

你需要做的是只复制列的属性,这个可以通过 isinstance(prop, sqlalchemy.orm.ColumnProperty) 来轻松筛选出来。需要注意的是,你必须复制外部存储的关系(所有的多对多关系),因为在主表中没有对应的列。这个操作不能通过高级接口完成,除非使用延迟加载,所以我建议你接受这个妥协。多对多关系可以通过 isinstance(prop, RelationProperty) and prop.secondary 来判断。最终的代码看起来会像这样:

from sqlalchemy.orm import object_mapper, ColumnProperty, RelationProperty

newobj = type(src)()
for prop in object_mapper(src).iterate_properties:
    if (isinstance(prop, ColumnProperty) or
        isinstance(prop, RelationProperty) and prop.secondary):
    setattr(newobj, prop.key, getattr(src, prop.key))

另外要注意的是,SQLAlchemy 设计的目的是为每个身份(即每个唯一标识)维护一个加载的对象,而你的复制操作在复制身份(主键)属性时打破了这一点。不过,如果你是用新的(版本化的)标识符来存储数据,这可能就不是问题了。

撰写回答