Reputation: 5176
I would like to migrate some data between two databases that share the same model. Here is my code:
from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
Base = declarative_base()
class Person(Base):
__tablename__ = 'person'
id = Column(Integer, primary_key=True)
name = Column(String(250), nullable=False)
# Create engines
source_engine = create_engine('sqlite:///source.db')
mirror_engine = create_engine('sqlite:///mirror.db')
# Create tables
Base.metadata.create_all(bind=source_engine)
Base.metadata.create_all(bind=mirror_engine)
# Create sessions
SourceSession = sessionmaker(bind=source_engine)
source_session = SourceSession()
MirrorSession = sessionmaker(bind=mirror_engine)
mirror_session = MirrorSession()
# Add data
source_session.add(Person(name="James"))
source_session.commit()
# Migrate data
results = source_session.query(Person).all()
for row in results:
mirror_session.add(row) # This line produces an error
mirror_session.commit()
# Close sessions
source_session.close()
mirror_session.close()
The migration line mirror_session.add(row)
produces the following error:
sqlalchemy.exc.InvalidRequestError: Object '<Person at 0x280a6b29898>' is already attached to session '1' (this is '2')
Upvotes: 0
Views: 238
Reputation: 52929
Your real use case might not be SQLite, but in case it is, you can perform such a migration without a round trip in Python with attached databases:
from sqlalchemy import create_engine, MetaData, event
from sqlalchemy import Table, Column, Integer, String
metadata = MetaData()
person = Table('person', metadata,
Column('id', Integer, primary_key=True),
Column('name', String(250), nullable=False))
doppelganger = person.tometadata(metadata, schema='mirror')
def attach_mirror(dbapi_conn, connection_rec):
dbapi_conn.execute("attach database 'mirror.db' as mirror")
engine = create_engine('sqlite:///source.db')
event.listen(engine, 'connect', attach_mirror)
metadata.create_all(engine)
with engine.connect() as conn:
conn.execute(person.insert().values(name='James'))
conn.execute(doppelganger.insert().prefix_with('OR IGNORE').
from_select(doppelganger.c, person.select()))
Upvotes: 1
Reputation: 5176
Use make_transient()
to remove all session information, then you can add it as expected.
from sqlalchemy.orm import make_transient
results = source_session.query(Person).all()
for row in results:
mirror_session.add(make_transient(row))
mirror_session.commit()
Upvotes: 0