【问题标题】:SQLAlchemy Declarative + relationships across multiple different databasesSQLAlchemy 声明式+跨多个不同数据库的关系
【发布时间】:2011-11-16 14:57:39
【问题描述】:

我花了一些时间,但我想出了如何使用 SQLAlchemy 对两种不同类型的数据库之间的关系进行建模:

Base = declarative_base()

class Survey(Base):
    __tablename__ = 'SURVEY'

    survey_id = Column("SURVEY_ID", Integer, primary_key=True)
    term_id = Column("TERM_ID", Integer, nullable=False)

    # Because the TERM table is in Oracle, but the SURVEY table is in
    # MySQL, I can't rely on SQLAlchemy's ForeignKey.  Thus,
    # I need to specify the relationship entirely by hand, like so:
    term = relationship("Term",
        primaryjoin="Term.term_id==Survey.term_id",
        foreign_keys=[term_id],
        backref="surveys"
    )

class Term(Base):
    __tablename__ = 'TERM'

    term_id   = Column(Integer, primary_key=True)
    term_name = Column(String(30))
    start_date = Column(Date)
    end_date = Column(Date)

mysql_engine = create_engine(MYSQL)
oracle_engine = create_engine(ORACLE)

Session = scoped_session(sessionmaker(
    binds={
        Term: oracle_engine,
        Survey: mysql_engine
    }
))

if __name__ == "__main__":
    survey = Session.query(Survey).filter_by(survey_id=8).one()
    print survey.term
    print survey.term.surveys

我必须这样做,因为 TERM 表位于我只有读取权限的 Oracle 数据库中,并且我正在编写一个应用程序来记录学生对该学期进行的调查。

上面的方法可行,但是当表的数量增加时它非常脆弱,因为 Session 需要准确地指定哪些映射的类对应于哪个引擎。我真的希望能够使用不同的Base 来定义哪些表属于哪个引擎,而不是单独绑定每个表。像这样:

mysql_engine = create_engine(MYSQL)
oracle_engine = create_engine(ORACLE)

MySQLBase = declarative_base(bind=mysql_engine)
OracleBase = declarative_base(bind=oracle_engine)

class Survey(MySQLBase):
    __tablename__ = 'SURVEY'

    survey_id = Column("SURVEY_ID", Integer, primary_key=True)
    term_id = Column("TERM_ID", Integer, nullable=False)


class Term(OracleBase):
    __tablename__ = 'ads_term_v'

    term_id   = Column(Integer, primary_key=True)
    term_name = Column(String(30))
    start_date = Column(Date)
    end_date = Column(Date)

Survey.term = relationship("Term",
    primaryjoin="Term.term_id==Survey.term_id",
    foreign_keys=[Survey.term_id],
    backref="surveys"
)

Session = scoped_session(sessionmaker())

if __name__ == "__main__":
    survey = Session.query(Survey).filter_by(survey_id=8).one()
    print survey.term
    print survey.term.surveys

很遗憾,这会导致查询运行时出现以下错误:

sqlalchemy.exc.InvalidRequestError: When initializing mapper Mapper|Survey|SURVEY, expression 'Term.term_id==Survey.term_id' failed to locate a name ("name 'Term' is not defined"). If this is a class name, consider adding this relationship() to the <class '__main__.Survey'> class after both dependent classes have been defined.

即使我确实在定义 Term 后将 relationship() 添加到调查中。

有人有什么建议吗?

【问题讨论】:

  • 理论上您可以使用metaclass 动态填充哪个类映射到哪个引擎的字典。或者通过MySQLBase.__subclasses_()绑定会话时动态检查MySQLBase/OracleBase的所有子类。 (没有测试过)

标签: database sqlalchemy relationship declarative


【解决方案1】:

这个回复可能很晚,但您可以将元数据与声明性基础分开定义,然后将其传递给两者。即:

meta = MetaData()
mysql_engine = create_engine(MYSQL)
oracle_engine = create_engine(ORACLE)

MySQLBase = declarative_base(bind=mysql_engine, metadata=meta)
OracleBase = declarative_base(bind=oracle_engine, metadata=meta)

【讨论】:

  • 嗯,当我问这个问题时,我不确定 4 年前是否可行。不过,对于未来的谷歌员工来说非常有用!
  • 未来的 googler 在这里:有没有办法可以使用这种技术来替换第三方模块中类的 Base 继承?我正在尝试编写一些代码,这些代码将受益于与另一个数据库对象的关系,但目前我想到的最好方法是复制该代码的模型类,唯一的变化是从不同的基础继承。
  • @mpounsett 我认为您所要求的不仅是可能的,而且是我使用它的目的。我从事这个项目已经有几年了,所以我不能说这项技术是否仍然有效。
【解决方案2】:

你不能。 AFAIK 没有针对两个不同数据库的单一查询。此外,您的模型必须共享相同的元数据实例才能在同一查询中使用。

也许您可以通过 ODBC 将 Oracle 数据库链接到数据库层上的 MySQL 数据库,然后您只需与 MySQL 对话。我从来没有这样做过,我不知道它是如何工作的。

您也可以独立查询两个数据库,并在应用层筛选和选择数据,以较少的工作为准。

【讨论】:

  • 我想可能是这样的。不过,我不知道您的模型需要共享它们的元数据才能在同一个查询中。很高兴知道这一点。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-06
  • 2011-10-10
  • 1970-01-01
  • 2018-02-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多