2013-06-14 5 views
6

Ho già fatto una domanda simile, ma ho pensato che potrei riformularlo, o mostrare quello che ho fatto per far luce su quello che sta succedendo qui.Database identici in Flask-SQLAlchemy

Attualmente ho 2 basi di dati identici, e ho tentato di risolvere il problema (come da un'altra domanda che ho visto) come questo:

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

class SubTable1(BaseTable): 
    __bind_key__ = 'bind1' 

class SubTable2(BaseTable): 
    __bind_key__ = 'bind2' 

Il problema di questo è che ora il più recente BIND è usato ovunque, quindi se lo faccio da qualche altra parte:

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

Quindi ottiene i risultati dal secondo database. Se dovessi cambiare la posizione delle classi SubTable, i risultati sono gli stessi (Modifica per chiarezza: con questo intendo che i risultati provengono da qualunque vincolo è definito per ultimo, se dovessero essere commutati, richiederebbe invece la query da 'bind2' invece di 'bind1' come fa attualmente). Non so davvero cosa fare, quindi se puoi aiutare in qualsiasi modo sarebbe fantastico.

Grazie.

MODIFICA: Se è impossibile (o semplicemente conosci un modo migliore o addirittura diverso) per farlo, faccelo sapere. Se potessi fare qualcosa come avere due oggetti db diversi, sarebbe anche meglio, semplicemente non so come farlo o quale tipo di implicazioni avrebbe.

EDIT 2: Dopo aver lavorato con questo per ore e ore, sono finalmente giunto a una conclusione su come farlo.

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) 

La ragione di questa assurdità è che si lega possono essere definiti solo una volta e non è cambiato, e non ci sono due nomi di tabella può essere lo stesso, anche se i bind sono diversi. Quindi devi creare 2 istanze di MetaData altrimenti SQLAlchemy si arrabbia. Quindi si scopre che il problema è una limitazione in SQLAlchemy.

risposta

8

Non so cosa sia __bind_key__, ma ci sono molti approcci all'utilizzo di una singola sessione con più collegamenti. La stessa sessione può essere associata direttamente: per fare questo, SubTable1 e SubTable2 devono essere mappati individualmente e non parte di una gerarchia di ereditarietà, in quanto la Session individua il bind basato sulla classe mappata alla base. Per condividere lo stesso MetaData, mappare entrambe le classi sullo stesso oggetto 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() 

questo è un modo. Un altro, facciamo in realtà basta usare due diversi oggetti di metadati, abbastanza facile con 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() 

e sì, dal momento che abbiamo i due oggetti metadati lì, siamo in grado di "legare" loro direttamente, se vogliamo seguire questa strada :

# ... 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()