On Jan 17, 2008, at 6:40 PM, Paul-Michael Agapow wrote:

>
>
> I'm puzzling out how to connect to multiple databases that have the
> same tables (and the same table definitions). The use case is that
> data may be retrieved from two or more similar dbs and perhaps copied
> between them. Following earlier advice, I've used a single metadata
> and 'bind' in the session creation to indicate which engine. However
> there's a problem. Example code below::
>
>       from sqlalchemy import *
>       from sqlalchemy.orm import *
>
>       uri1 = # blah blah
>       uri2 =  # blah blah
>
>       class Dummy (object):
>               "simple mapped class"
>               pass
>       
>       class DbConnection (object):
>               "encapsulate engine & session"
>               def __init__ (self, uri, dbschema):
>                       engine = create_engine (uri)
>                       Session = sessionmaker (bind=engine, autoflush=False,
> transactional=False)
>                       self._session = Session()
>                       dbschema.metadata.create_all (bind=engine, 
> checkfirst=True)
>               
>               def __del__ (self):
>                       self._session.close()
>
>
>       class DbSchema (object):
>               "metadata & table defintions"
>               def __init__ (self):
>                       self.metadata = MetaData()
>                       self.dummy_table = Table('dummy', self.metadata,
>                               Column('id', Integer, primary_key=True),
>                               Column('name', Unicode(40)),
>                       )
>                       mapper (Dummy, self.dummy_table)
>               
>       # define tables and connect to both dbs
>       schema = DbSchema()
>       conn1 = DbConnection (uri1, schema)
>       conn2 = DbConnection (uri2, schema)
>
>       # create object
>       x = Dummy()
>       x.name = u'testname'
>
>       # save to one and detach object
>       conn1._session.save_or_update (x)
>       conn1._session.flush ()
>       conn1._session.expunge (x)
>
>       # save to two silently fails!
>       conn2._session.save_or_update (x)
>       conn2._session.flush ()
>       conn2._session.expunge (x)
>
>
> The object saves obediently to the first db, but fails to save to the
> second (although tables are created correctly). The successful write
> is always the first one, regardless of the order of opening
> connections. It doesn't depend on how the primary key is defined:
> autogenerated or supplied. I therefore surmise this is because
> 'save_or_update' detects the object as already being persistent,
> despite the use of 'expunge'.

thats correct, an object has an "_instance_key" attribute which marks  
it as "persistent".

> Is there another step required to detach the object, or is another
> approach necessary to handle multiple dbs?

the particular way youd like to handle "multiple dbs" here is that  
youre trying to replicate a full set of operations between two of  
them.  we dont have a "recommended" way to approach this, other than  
usually replication is handled at the database level, not the python  
level, using the replication abilities of the database itself or  
external tools designed for this purpose.   But Im accustomed to  
people rejecting that suggestion when this kind of thing comes up, so  
heres some things you can try in python:

you can try putting objects into the session using session.merge(),  
which creates a copy of the object in the session, leaving the given  
object unchanged, where it could then be merged into a second  
session.   Another approach would be to manually remove the  
"_instance_key" attribute before saving twice, but this can lead to  
complexities when instances with relations to other instances are  
manipulated (mostly because we dont currently "support" that use case  
and havent created tests against it).  Finally, you can wrap up the  
full set of operations (i.e. the full sequence of create object,  
populate, save) into a single callable which is called twice, being  
passed two different sessions.

Another approach which is technically feasable but is not implemented  
right now is to create a proxying engine (or really, just a proxying  
Connection would do) which distributes all SQL calls to more than one  
engine.  If you were willing to write a little more code you could go  
this route as well, binding just a single Session to your proxying  
Connection which routes all "execute" calls to two or more embedded,  
actual Connection objects.  This would provide the most seamless  
experience but would require a little more tinkering.








--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"sqlalchemy" group.
To post to this group, send email to sqlalchemy@googlegroups.com
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to