让我们考虑 3 个表:
每本书都有一个其作者的外键,可以在美国表中,也可以在英国表中。
如何在 SQLAlchemy 中实现这样的外键条件?
我想要一个单独的列来处理链接。
到目前为止,我的方法是创建一个抽象类
Author
,AmericanAuthor
和BritishAuthor
都继承自该抽象类,并且让Book
的外键指向父级。
class Author(Model):
__abstract__ = True
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String)
class AmericanAuthor(Author):
__tablename__ = 'american_author'
# some other stuff
class BritishAuthor(Author):
__tablename__ = 'british_author'
# some other stuff
class Book(Model):
__tablename__ = 'book'
title = db.Column(db.String)
author_id = db.Column(db.Integer, db.ForeignKey("author.id"))
失败并出现以下错误:
sqlalchemy.exc.NoReferencedTableError: Foreign key associated with column 'books.author_id' could not find table 'author' with which to generate a foreign key to target column 'id'
这完全有道理,考虑到
author
是抽象的......
虽然
@property
装饰器可以在应用程序中工作,但使用 @hybrid_property
包中的 sqlalchemy.ext.hybrid
可能会更好。通过这种方式,您将能够像任何普通属性一样过滤该属性。
您的 Book 类将如下所示:
class Book(Model):
__tablename__ = 'book'
title = db.Column(db.String)
american_author_id = db.Column(db.Integer, db.ForeignKey("american_author.id"), nullable=True)
british_author_id = db.Column(db.Integer, db.ForeignKey("british_author.id"), nullable=True)
@hybrid_property
def author_id(self):
return self.american_author_id or self.british_author_id
我认为您无法使用同一列与两个不同的表建立关系。
尝试创建两个不同的列(“american_author_id”和“british_author_id”),然后创建一个 @property“author”,返回不为 NULL 的作者。
这样你就可以让作者使用:
mybook.author
您应该能够通过使用
AbstractConcreteBase
和 primaryjoin
来解决这个问题。
https://docs.sqlalchemy.org/en/20/orm/inheritance.html
class Author(AbstractConcreteBase, Base):
id: Mapped[int] = mapped_column(primary_key=True)
name = db.Column(db.String)
@declared_attr
def books(cls) -> Mapped[List["Book"]]:
return relationship(
"Book",
back_populates="author",
primaryjoin=lambda: and_(foreign(Book.author_id) == cls.id)
)
class AmericanAuthor(Author):
__tablename__ = 'american_author'
# some other stuff
__mapper_args__ = {
"polymorphic_identity": "american",
"concrete": True,
}
class BritishAuthor(Author):
__tablename__ = 'british_author'
# some other stuff
__mapper_args__ = {
"polymorphic_identity": "british",
"concrete": True,
}
class Book(Model):
__tablename__ = 'book'
title: Mapped[str]
author_id: Mapped[int] # Not a foreign key
author: Mapped["Author"] = relationship(
back_populates="books",
primaryjoin="and_(foreign(Book.author_id)==Author.id)"
)
有了这个,你应该能够做到
book.author
和 author.books