SQLAlchemy декларативные + отношения между несколькими различными базами данных - PullRequest
11 голосов
/ 14 сентября 2011

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

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

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

Ответы [ 2 ]

5 голосов
/ 27 сентября 2015

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

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)
3 голосов
/ 16 сентября 2011

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

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

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

...