如何让SQLAlchemy的associationproxy始终调用creator而非修改属性?
问题:通过字符串键切换关联实例而非修改属性
想要构建一个以字符串键表示关联关系的模型,修改该键时应创建或加载实例,而非直接修改属性。尝试用associationproxy实现,但测试发现修改category_name时会直接修改原Category实例的name属性,而非切换实例。已知可传入getset_factory但无示例,求实现方式或更优方案。
现有代码
Category 模型
class Category(db.Model): id: orm.Mapped[int] = orm.mapped_column(primary_key=True) name: orm.Mapped[str] = orm.mapped_column(sa.String(255), unique=True) @classmethod def load_or_create(cls, name): # 原代码缺少return,会导致创建新实例时返回None,需修正 return cls.query.filter_by(name=name).first() or cls(name=name)
Thing 模型
class Thing(db.Model): id: orm.Mapped[int] = orm.mapped_column(primary_key=True) category_id: orm.Mapped[int] = orm.mapped_column(sa.ForeignKey("category.id")) category: orm.Mapped["Category"] = orm.relationship() category_name = sqlalchemy.ext.associationproxy.association_proxy("category", "name", creator=Category.load_or_create)
测试用例
def test_category_name(): thing = Thing(category_name="foo") assert thing.category.name == "foo" # 目前正常 foo_category = thing.category thing.category_name = "bar" # 此处失败,因为修改了foo_category的name而非切换实例 assert thing.category is not foo_category
解决方案:自定义getset_factory覆盖默认行为
associationproxy默认逻辑是直接修改关联对象的指定属性,要实现切换实例,需自定义getset_factory,让设置category_name时调用creator获取新实例并赋值给category,而非修改原实例属性。
修改Thing模型中的category_name定义:
from sqlalchemy.ext.associationproxy import association_proxy class Thing(db.Model): id: orm.Mapped[int] = orm.mapped_column(primary_key=True) category_id: orm.Mapped[int] = orm.mapped_column(sa.ForeignKey("category.id")) category: orm.Mapped["Category"] = orm.relationship() category_name = association_proxy( "category", "name", creator=Category.load_or_create, getset_factory=lambda proxy: ( # 获取逻辑:返回关联实例的name,无关联则返回None lambda: proxy.obj.category.name if proxy.obj.category else None, # 设置逻辑:调用load_or_create获取实例,直接替换关联对象 lambda name: setattr(proxy.obj, "category", Category.load_or_create(name)) ) )
说明
- 自定义的
getset_factory返回元组,包含值的获取函数与设置函数 - 设置函数不再修改原
Category的name,而是通过load_or_create获取对应名称的实例,直接替换thing.category,从而实现实例切换 - 修正原
load_or_create的return缺失问题,确保创建新实例时能正确返回对象
内容的提问来源于stack exchange,提问作者zwirbeltier
相关产品推荐
相关产品推荐

