assigning specific column to be primary key when reflecting an existing table with sqlalchemy and turbogears

StackOverflow https://stackoverflow.com/questions/8195300

  •  04-03-2021
  •  | 
  •  

Question

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

Was it helpful?

Solution

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)
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top