Sqlalchemy、继承与关系

4 投票
1 回答
5863 浏览
提问于 2025-04-16 11:01

我有一个通用的用户对象,这个对象是我网站的基础类,使用了连接表的方式来构建。


class User(Base):
    __tablename__ = "auth_user"

    id = Column(Integer, primary_key = True)
    username = Column(String(100), nullable = False, unique = True, index = True)
    ...
    type = Column(String(1))
    __mapper_args__ = {'polymorphic_on' : type, "extension" : HashExtension()}

然后我基于这个类创建了一个员工对象。


class Staff(User):
    __tablename__ = "auth_staff"
    __mapper_args__ = {'polymorphic_identity' : 's'}    
    id = Column(Integer, ForeignKey('auth_user.id'), primary_key = True)

现在我又有一个候选人对象,同样是从用户对象派生出来的。


class Candidate(User):
    __tablename__ = "candidates_candidate"
    __mapper_args__ = {'polymorphic_identity' : 'c'}
    id = Column(Integer, ForeignKey('auth_user.id'), primary_key = True)
    ...
    staff_id = Column(Integer, ForeignKey("auth_user.id"), nullable = False)

    staff = relationship("Staff", backref = backref("candidates", order_by = id))

到目前为止,候选人对象的部分都没问题。我想让它能链接回员工对象,但我遇到了关于没有“primaryjoin”的错误,我对这个关系应该如何链接到员工对象感到困惑——是不是应该链接到用户对象,因为员工是从用户派生的呢?……

任何建议我都会非常感激。

~~~~~~~~~更新 2月3日~~~~~~~~~~~~~~

修订后的代码 - 仍然抛出关于primaryjoin的错误。如果我添加了primaryjoin,它还是会出错。


#!/usr/bin/env python
from sqlalchemy import Column, Integer, String, ForeignKey, Boolean
from sqlalchemy.orm import relationship, backref, sessionmaker
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine

class DomainBase(object):
    active = Column(Boolean, default = True, nullable = False)

    def __str__(self):
        return repr(self)

Base = declarative_base(cls = DomainBase)

class User(Base):
    __tablename__ = "auth_user"

    id = Column(Integer, primary_key = True)
    username = Column(String(100), nullable = False, unique = True, index = True)
    type = Column(String(1))
    __mapper_args__ = {'polymorphic_on' : type}
class Staff(User):
    __tablename__ = "auth_staff"
    __mapper_args__ = {'polymorphic_identity' : 's'}    
    id = Column(Integer, ForeignKey('auth_user.id'), primary_key = True)


class Candidate(User):
    __tablename__ = "candidates_candidate"
    __mapper_args__ = {'polymorphic_identity' : 'c'}
    id = Column(Integer, ForeignKey('auth_user.id'), primary_key = True)
    staff_id = Column(Integer, ForeignKey("auth_staff.id"), nullable = False)

    staff = relationship("Staff", backref = backref("candidates", order_by = id))


engine = create_engine('sqlite:///:memory:')
Base.metadata.create_all(engine)

Session = sessionmaker(bind = engine, autocommit=True)
session = Session()

with session.begin():
    s = Staff(username = "DaveSmith")
    session.add_all([s])

1 个回答

4

你的例子中使用了两个外键指向基类表,所以 SQLAlchemy 无法判断 1) 应该用哪个外键来进行继承连接,2) 应该用哪个外键来处理员工关系。你需要为这两种情况提供提示。第一个情况需要在 __mapper_args__ 中设置 'inherit_condition' 选项(更多信息可以查看这个回答),第二个情况则需要在 relationship 中添加 primaryjoin=(staff_id==User.id) 参数。

但是要注意,你的 staff 关系是指向 Staff 类的,而 staff_id 是指向 User 表的外键。虽然你可能有某种理由这样做,但在大多数情况下这样并不好。staff_id 的定义改为使用 ForeignKey("auth_staff.id") 可以解决你的问题,而不需要其他更改。

撰写回答