SQLAlchemy декларативные + отношения между несколькими различными базами данных

мне потребовалось некоторое время, но я понял, как использовать 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

Я должен сделать это, потому что таблица терминов находится в базе данных Oracle, на которой у меня есть только доступ для чтения, и я пишу приложение, которое записывает опросы, сделанные студентами, об этом термине.

вышеизложенное работает, но это очень хрупко, когда количество таблиц поднимается вверх, так как сеанс должен точно указать, какие сопоставленные классы соответствуют какому двигателю. Я бы очень хотел иметь возможность использовать другой 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.

хотя я сделал добавить отношение () к опросу после определения термина.

есть ли у кого-нибудь предложения?

2 ответов


ты не можешь. AFAIK нет единого запроса к двум разным базам данных. Кроме того, ваши модели должны использовать один и тот же экземпляр метаданных для использования в одном запросе.

возможно, вы можете связать Oracle db с MySQL db на уровне DB через ODBC, тогда вы будете говорить только с MySQL. Я никогда этого не делал и не знаю, как это работает.

вы также можете запрашивать обе базы данных независимо и фильтровать и выбирать данные на уровне приложения, в зависимости от того, что меньше работа.


возможно, очень поздно с этим ответом, но вы могли бы определить метаданные отдельно от декларативной базы, а затем передать их на оба. т. е.:

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)