6

I've already asked a similar question, but I thought maybe I could rephrase it, or show what I've done further to shed some light onto what's going on here.

Currently I have 2 identical databases, and I've attempted to solve the problem (as per another question I saw) like this:

class BaseTable(db.Model):
    __tablename__ = 'TableName'
    col = db.Column(db.Integer)

class SubTable1(BaseTable):
    __bind_key__ = 'bind1'

class SubTable2(BaseTable):
    __bind_key__ = 'bind2'

The problem with this is that now the most recent bind is used everywhere, so if I do this somewhere else:

SubTable1.query.filter_by(col=12).all()

Then it gets results from the second database. If I were to switch the locations of the SubTable classes, then the results are the same (Edit for clarity: by which I mean that the results come from whatever bind is defined last, if they were to be switched, it would instead query from 'bind2' instead of 'bind1' as it currently does). I don't really know what to do, so if you can help in any way that would be awesome.

Thanks.

EDIT: If it's impossible (or you simply know a better or even different way) to do this, please let me know. If I could do something like having two different db objects, that would be good as well, I just don't really know how to do that or what kind of implications that would have.

EDIT 2: After toiling with this for hours and hours, I've finally come to a conclusion on how to do this.

In __init__.py:

db1 = SQLAlchemy(app)
db2 = SQLAlchemy(app)

In models.py:

class Table1(db1.Model):
    __tablename__ = 'TableName'
    __bind_key__ = 'bind1'
    col = db1.Column(db1.Integer)

class Table2(db2.Model):
    __tablename__ = 'TableName'
    __bind_key__ = 'bind2'
    col = db2.Column(db2.Integer)

The reason for this nonsense is that binds can only be defined once and not changed, and no two table names can be the same, even if the binds are different. So you have to make 2 MetaData instances or else SQLAlchemy gets mad. So it turns out the problem is a limitation in SQLAlchemy.

4

1 回答 1

8

我不知道是什么__bind_key__,但是有很多方法可以将单个 Session 与多个绑定一起使用。Session 本身可以直接绑定:为此,SubTable1 和 SubTable2 需要单独映射,而不是继承层次结构的一部分,因为 Session 基于最基映射的类定位绑定。为了共享相同的 MetaData,只需将两个类映射到相同的 Table 对象:

from sqlalchemy import *
from sqlalchemy.orm import *
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

class BaseTable(Base):
    __tablename__ = 'some_table'
    id = Column(Integer, primary_key=True)

class SubTable1(Base):
    __table__ = BaseTable.__table__

class SubTable2(Base):
    __table__ = BaseTable.__table__

db1 = create_engine("sqlite:///db1.db", echo=True, logging_name='db1')
db2 = create_engine("sqlite:///db2.db", echo=True, logging_name='db2')

Base.metadata.create_all(db1)
Base.metadata.create_all(db2)

s = Session(binds={SubTable1: db1, SubTable2: db2})

s.add_all([
    SubTable1(),
    SubTable2(),
    SubTable1(),
    SubTable2(),
    SubTable1(),
])

s.commit()

print s.query(SubTable1).all()
print s.query(SubTable2).all()

这是一种方式。另一个,让我们实际上只使用两个不同的 MetaData 对象,使用 mixins 很容易:

from sqlalchemy import *
from sqlalchemy.orm import *
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

class BaseTable(object):
    __tablename__ = 'some_table'
    id = Column(Integer, primary_key=True)

class DB1(Base):
    metadata = MetaData()
    __abstract__ = True

class DB2(Base):
    metadata = MetaData()
    __abstract__ = True

class SubTable1(BaseTable, DB1):
    pass

class SubTable2(BaseTable, DB2):
    pass

db1 = create_engine("sqlite:///db1.db", echo=True, logging_name='db1')
db2 = create_engine("sqlite:///db2.db", echo=True, logging_name='db2')

DB1.metadata.create_all(db1)
DB2.metadata.create_all(db2)

s = Session(binds={SubTable1: db1, SubTable2: db2})

s.add_all([
    SubTable1(),
    SubTable2(),
    SubTable1(),
    SubTable2(),
    SubTable1(),
])

s.commit()

print s.query(SubTable1).all()
print s.query(SubTable2).all()

是的,因为我们在那里有两个 MetaData 对象,如果我们想走那条路,我们可以直接“绑定”它们:

# ... mapping as before

DB1.metadata.bind = db1
DB2.metadata.bind = db2
DB1.metadata.create_all()
DB2.metadata.create_all()

s = Session()  # don't need binds in this case

# ... usage as before
s = Session()
于 2013-06-17T05:28:44.763 回答