this is part of a project that involves working with tg2 against 2 databases one of them (which this model uses is mssql). since that table I need to read/write from is created and managed with a different application I don't want turbogears to overwrite/change the table - just work with the existing table - so I use sqlalchemy magical 'autoload' reflection (I also don't know every detail of this table configuration in the mssql db)
some of the reflection is done in model.__init__.py
and not in the class (as some sqlalchemy tutorial suggest) because of tg2 innerworking
this is the error message I get:(the table name in db is SOMETABLE and in my app the class is activities)
sqlalchemy.exc.ArgumentError: Mapper Mapper|Activities|SOMETABLE could not
assemble any primary key columns for mapped table 'SOMETABLE'
this is activities class:
class Activities(DeclarativeBase2):
__tablename__ = 'SOMETABLE'
#tried the classic way, I used in another place without tg but didn't work here - the reflection should be outside the class
#__table_args__= {'autoload':True
#,'autoload_with':engine2
#}
def __init__(self,**kw):
for k,v in kw.items():
setattr(self,k,v)
and this is model.__init__.py
init model method (where the reflection is called):
def init_model(engine1,engine2):
"""Call me before using any of the tables or classes in the model."""
DBSession.configure(bind=engine1)
DBSession2.configure(bind=engine2)
# If you are using reflection to introspect your database and create
# table objects for you, your tables must be defined and mapped inside
# the init_model function, so that the engine is available if you
# use the model outside tg2, you need to make sure this is called before
# you use the model.
#
# See the following example:
metadata.bind = engine1
metadata2.bind = engine2
#metadata2=MetaData(engine2)
global t_reflected
#
t_reflected = Table("SOMETABLE", metadata2,
autoload=True, autoload_with=engine2)
#
mapper(Activities, t_reflected
so I think I need to tell sqlalchemy what is the primary key - but how do I do it while using the reflection (I know the primary key field)?
EDIT the working solution:
def init_model(engine1,engine2):
"""Call me before using any of the tables or classes in the model."""
DBSession.configure(bind=engine1)
DBSession2.configure(bind=engine2)
# If you are using reflection to introspect your database and create
# table objects for you, your tables must be defined and mapped inside
# the init_model function, so that the engine is available if you
# use the model outside tg2, you need to make sure this is called before
# you use the model.
#
# See the following example:
metadata.bind = engine1
metadata2.bind = engine2
#metadata2=MetaData(engine2)
global t_reflected
#
t_reflected = Table("SOMETABLE", metadata2,String,primary_key=True),
autoload=True, autoload_with=engine2)# adding the primary key column here didn't work
#
mapper(Activities, t_reflected, non_primary=True)# notice the needed non_primary - for some reason I can't do the whole mapping in one file and I Have to do part in init_model and part in the model - quite annoying
also in the model I had to add the primary key column making it:
class Activities(DeclarativeBase2):
__tablename__ = 'SOMETABLE'
#tried the classic way, I used in another place without tg but didn't work here - the reflection should be outside the class
EVENTCODE = Column(String, primary_key=True)# needed because the reflection couldn't find the primary key .
of course I also had to add various imports in model.__init__.py
to make this work
the strange thing is it turned out that it complained about not finding a primary key before it even connected to the db and when a standalone sqlalchemy class (without tg2) doing the same key - didn't complain at all. makes you wonder
You can mix-and-match: see Overriding Reflected Columns in the documentation. In your case the code would look similar to this:
t_reflected = Table("SOMETABLE", metadata2,
Column('id', Integer, primary_key=True), # override reflected '???' column to have primary key
autoload=True, autoload_with=engine2)
edit-1: Model version: I also think that only declarative version should work as well, in which case you should not define a table t_reflected
and also should not map those manually using mapper(...)
because declarative classes are automatically mapped:
class Activities(DeclarativeBase2):
__table__ = Table('SOMETABLE', metadata2,
Column('EVENTCODE', Unicode, primary_key=True),
autoload=True, autoload_with=engine2,
)
def __init__(self,**kw):
for k,v in kw.items():
setattr(self,k,v)