如何用SQLAlchemy ORM将一行所有属性复制到另一已有行
问题:Flask-SQLAlchemy实现配置文件复制覆盖"current"行
我正在尝试使用Flask-SQLAlchemy为Web应用实现基于配置文件的持久化配置,思路是通过ConfigProfiles表存储管理员创建的所有配置文件,同时保留一个名为"current"的行代表运行时配置。激活配置文件时,需要将目标配置文件的所有值复制并覆盖"current"行的数据。
当前的模型类代码如下:
class ConfigProfiles(db.Model): name = db.Column(db.String(32), primary_key=True) config_option_1 = db.Column(db.Integer, default=1) config_option_2 = db.Column(db.Boolean, default=False) # ... 更多配置列 CUR_PROFILE_NAME = "current" @classmethod def get_current(cls): current_config = cls.query.filter(cls.name == cls.CUR_PROFILE_NAME).one_or_none() # 若不存在则创建带默认值的"current"行 if current_config is None: current_config = cls(name=cls.CUR_PROFILE_NAME) db.session.add(current_config) db.session.commit() return current_config # ... 其他方法
我想添加一个实例方法处理配置文件激活,但不想逐个更新列(避免新增列时需要修改代码)。之前尝试的代码会修改原行主键,大概率触发完整性错误:
# ... 在ConfigProfiles类中 def activate(self): if self.name != "current": self.name = "current" db.session.merge(self) db.session.commit()
请问如何正确实现将配置文件行复制并覆盖已有的"current"行?
解决方案
你需要避免修改原配置文件的主键,而是动态复制所有非主键字段的值到"current"行中,这样新增配置列时无需修改激活逻辑。可以通过遍历模型表的列来实现通用的字段复制:
def activate(self): if self.name == self.CUR_PROFILE_NAME: return # 无需激活自身 current_config = self.get_current() # 遍历所有列,跳过主键name,复制字段值 for column in self.__table__.columns: column_key = column.key if column_key != 'name': setattr(current_config, column_key, getattr(self, column_key)) db.session.commit()
逻辑说明:
- 先判断当前实例是否是"current"本身,是则直接返回,避免无效操作
- 通过
get_current()获取或创建"current"配置实例 - 遍历模型对应的数据库表的所有列,排除主键
name - 使用
setattr和getattr动态复制每个字段的值到"current"实例 - 提交会话完成更新
这种方式的优势是无需硬编码配置列,后续新增config_option_3等字段时,激活方法不需要做任何修改,完全适配新字段。
另外,你之前尝试的merge方法之所以不可行,是因为修改实例主键后,merge会尝试将该实例与数据库中已有的"current"行合并,导致主键冲突;或者如果"current"行不存在,会修改原配置文件行的主键为"current",这完全不符合"复制覆盖"的需求。
内容的提问来源于stack exchange,提问作者spicy.dll
相关产品推荐
相关产品推荐

