【问题标题】:Self-referential foreign key relationship on SQLAlchemy declarative mixin class with declared_attrSQLAlchemy声明性mixin类上的自引用外键关系与declared_attr
【发布时间】:2018-11-14 18:17:19
【问题描述】:

我在 SQLAlchemy 应用程序的开头附近定义了一个 mixin 类,然后继承了我使用的几乎所有声明性模型。

class Owned(object):

    @declared_attr
    def created_by_id(cls):
        return Column(Integer, ForeignKey("accounts.id"),
            nullable = True)

    @declared_attr
    def created_by(cls):
        return relationship("Account", foreign_keys = cls.created_by_id)

    @declared_attr
    def updated_by_id(cls):
        return Column(Integer, ForeignKey("accounts.id"),
            nullable = True)

    @declared_attr
    def updated_by(cls):
        return relationship("Account", foreign_keys = cls.updated_by_id)

这适用于大多数预期用例。

class Thing(Owned, Base): # Base is from SQLAlchemy's declarative_base()
    pass

account = session.query(Account).first()

thing = Thing(created_by = account, updated_by = account)

session.add(thing)
session.commit()
session.refresh(thing)

assert thing.created_by == account # pass
assert thing.updated_by == account # pass

但是,当我将 Account 本身定义为从 Owned 继承时,我得到了意外的行为。

class Account(Owned, Base):
    pass

account_old = session.query(Account).first()

account_new = Account(created_by = account_old, updated_by = account_old)

session.add(account_new)
session.commit()
session.refresh(account_new)

assert account_new.created_by_id == account_old.id # pass
assert account_new.updated_by_id == account_old.id # pass

# BUT!

assert account_new.created_by == account_old # fail
assert account_new.updated_by == account_old # fail

account_new.created_by # []
account_new.updated_by # []

我看到,在这种情况下,我已将 created_by_idupdated_by_id 转换为自引用外键。然而,我不明白的是,为什么 SQLAlchemy 没有用预期的 Account 实例填充它们关联的 relationship 列。

我做错了什么?

【问题讨论】:

    标签: python sqlalchemy self-referencing-table


    【解决方案1】:

    adjacency list relationship 中,默认情况下假定“方向”为一对多。使用 remote_side 指令确定关系是多对一的,这就是您所追求的:

    def _create_relationship(cls, foreign_keys):
        kwgs = {}
        # Bit of a chicken or egg situation:
        if cls.__name__ == "Account":
            kwgs["remote_side"] = [cls.id]
    
        return relationship("Account", foreign_keys=foreign_keys, **kwgs)
    
    
    class Owned:
    
        @declared_attr
        def created_by_id(cls):
            return Column(Integer, ForeignKey("accounts.id"),
                nullable = True)
    
        @declared_attr
        def created_by(cls):
            return _create_relationship(cls, cls.created_by_id)
    
        @declared_attr
        def updated_by_id(cls):
            return Column(Integer, ForeignKey("accounts.id"),
                nullable = True)
    
        @declared_attr
        def updated_by(cls):
            return _create_relationship(cls, cls.updated_by_id)
    

    【讨论】:

    • 非常感谢。我遵循了您的方法并收到此错误:sqlalchemy.exc.ArgumentError: Relationship Account.created_by could not determine any unambiguous local/remote column pairs based on join condition and remote_side arguments. Consider using the remote() annotation to accurately mark those elements of the join condition that are on the remote side of the relationship. 我是否必须手动指定加入?还是foreign_keys 只在一个方向上被识别?
    • 嗯,我认为需要更多的上下文。鉴于 2 个关系属性,mixin 在我的本地测试中工作,不需要任何手动连接。
    猜你喜欢
    • 2012-02-29
    • 1970-01-01
    • 2021-11-05
    • 2018-03-03
    • 1970-01-01
    • 2016-07-11
    • 2011-03-23
    • 2013-09-19
    • 2014-09-30
    相关资源
    最近更新 更多