Pyramid: DBSession.add(model) returns Type Error
Question: When I run the `initializedb.py` script, my tables are created fine but when I
try to insert any data, I get the following error:

    
    
    TypeError: unbound method after_attach() must be called with ZopeTransactionExtension instance as first argument (got Session instance instead)
    

Why am I getting this error? I tried googling the error but the only example
related to me I found had to do with an incorrect query.

My package structure and relevant sections of code are as follows:

In my Pyramid application, instead of defining all models in the `models.py`
file, I've created a separate package called models and put all my model
classes there.

Basically, I have

    
    
    myapp/
        models/
            __init__.py
            meta.py
            school.py
        __init__.py
    

My main `__init__.py` has:

    
    
    from myapp.models.meta import (
        DBSession,
        Base,
        )
    

Similarly, the `__init__.py` file inside `models` has:

    
    
    from .meta import DBSession
    from .school import School
    

`meta.py` inside `models` has:

    
    
    from sqlalchemy.ext.declarative import declarative_base
    from sqlalchemy.orm import (
        scoped_session,
        sessionmaker,
        )
    from zope.sqlalchemy import ZopeTransactionExtension
    
    Base = declarative_base()
    DBSession = scoped_session(sessionmaker(extension=ZopeTransactionExtension))
    

The model file `school.py` has:

    
    
    from .meta import (
        Base,
        get_prefix,
        )
    
    class School(Base):
        """ The SQLAlchemy declarative model class for a School object. """
        __tablename__ = 'schools'
    
        id = Column(Integer, primary_key=True)
        school_code = Column(String(10))
    

And, finally, the `scripts/initializedb.py` file has:

    
    
    from myapp.models.meta import (
        Base,
        DBSession,
        )
    
    from myapp.models import (
        School,
        )
    def main(argv=sys.argv):
        if len(argv) < 2:
            usage(argv)
        config_uri = argv[1]
        options = parse_vars(argv[2:])
        setup_logging(config_uri)
        settings = get_appsettings(config_uri, options=options)
        engine = engine_from_config(settings, 'sqlalchemy.')
        DBSession.configure(bind=engine)
        Base.metadata.create_all(engine)
        with transaction.manager:
            demo_school = School(school_code="SC-123")
            DBSession.add(demo_school)
    

**EDIT:**

The full traceback is as follows:

    
    
    2014-06-22 20:18:31,577 INFO  [sqlalchemy.engine.base.Engine][MainThread] SHOW VARIABLES LIKE 'sql_mode'
    2014-06-22 20:18:31,577 INFO  [sqlalchemy.engine.base.Engine][MainThread] ()
    2014-06-22 20:18:31,585 DEBUG [sqlalchemy.engine.base.Engine][MainThread] Col ('Variable_name', 'Value')
    2014-06-22 20:18:31,586 DEBUG [sqlalchemy.engine.base.Engine][MainThread] Row ('sql_mode', '')
    2014-06-22 20:18:31,586 INFO  [sqlalchemy.engine.base.Engine][MainThread] SELECT DATABASE()
    2014-06-22 20:18:31,586 INFO  [sqlalchemy.engine.base.Engine][MainThread] ()
    2014-06-22 20:18:31,587 DEBUG [sqlalchemy.engine.base.Engine][MainThread] Col ('DATABASE()',)
    2014-06-22 20:18:31,587 DEBUG [sqlalchemy.engine.base.Engine][MainThread] Row ('myapp',)
    2014-06-22 20:18:31,588 INFO  [sqlalchemy.engine.base.Engine][MainThread] show collation where `Charset` = 'utf8' and `Collation` = 'utf8_bin'
    2014-06-22 20:18:31,588 INFO  [sqlalchemy.engine.base.Engine][MainThread] ()
    2014-06-22 20:18:31,590 DEBUG [sqlalchemy.engine.base.Engine][MainThread] Col ('Collation', 'Charset', 'Id', 'Default', 'Compiled', 'Sortlen')
    2014-06-22 20:18:31,591 DEBUG [sqlalchemy.engine.base.Engine][MainThread] Row ('utf8_bin', 'utf8', 83L, '', 'Yes', 1L)
    2014-06-22 20:18:31,592 INFO  [sqlalchemy.engine.base.Engine][MainThread] SELECT CAST('test plain returns' AS CHAR(60)) AS anon_1
    2014-06-22 20:18:31,592 INFO  [sqlalchemy.engine.base.Engine][MainThread] ()
    2014-06-22 20:18:31,593 INFO  [sqlalchemy.engine.base.Engine][MainThread] SELECT CAST('test unicode returns' AS CHAR(60)) AS anon_1
    2014-06-22 20:18:31,593 INFO  [sqlalchemy.engine.base.Engine][MainThread] ()
    2014-06-22 20:18:31,594 INFO  [sqlalchemy.engine.base.Engine][MainThread] SELECT CAST('test collated returns' AS CHAR CHARACTER SET utf8) COLLATE utf8_bin AS anon_1
    2014-06-22 20:18:31,595 INFO  [sqlalchemy.engine.base.Engine][MainThread] ()
    2014-06-22 20:18:31,596 INFO  [sqlalchemy.engine.base.Engine][MainThread] DESCRIBE `a3fhs32g_schools`
    2014-06-22 20:18:31,597 INFO  [sqlalchemy.engine.base.Engine][MainThread] ()
    2014-06-22 20:18:31,598 DEBUG [sqlalchemy.engine.base.Engine][MainThread] Col ('Field', 'Type', 'Null', 'Key', 'Default', 'Extra')
    2014-06-22 20:18:31,598 DEBUG [sqlalchemy.engine.base.Engine][MainThread] Row ('id', 'int(11)', 'NO', 'PRI', None, 'auto_increment')
    2014-06-22 20:18:31,598 INFO  [sqlalchemy.engine.base.Engine][MainThread] DESCRIBE `a3fhs32g_students`
    2014-06-22 20:18:31,598 INFO  [sqlalchemy.engine.base.Engine][MainThread] ()
    2014-06-22 20:18:31,599 DEBUG [sqlalchemy.engine.base.Engine][MainThread] Col ('Field', 'Type', 'Null', 'Key', 'Default', 'Extra')
    2014-06-22 20:18:31,599 DEBUG [sqlalchemy.engine.base.Engine][MainThread] Row ('id', 'int(11)', 'NO', 'PRI', None, 'auto_increment')
    
    Traceback (most recent call last):
      File "../bin/initialize_myapp_db", line 9, in <module>
        load_entry_point('myapp==0.0', 'console_scripts', 'initialize_myapp_db')()
      File "/var/www/html/myapp/app/myapp/myapp/myapp/scripts/initializedb.py", line 44, in main
        DBSession.add(model)
      File "/var/www/html/myapp/app/myapp/local/lib/python2.7/site-packages/SQLAlchemy-0.9.4-py2.7-linux-i686.egg/sqlalchemy/orm/scoping.py", line 149, in do
        return getattr(self.registry(), name)(*args, **kwargs)
      File "/var/www/html/myapp/app/myapp/local/lib/python2.7/site-packages/SQLAlchemy-0.9.4-py2.7-linux-i686.egg/sqlalchemy/orm/session.py", line 1478, in add
        self._save_or_update_state(state)
      File "/var/www/html/myapp/app/myapp/local/lib/python2.7/site-packages/SQLAlchemy-0.9.4-py2.7-linux-i686.egg/sqlalchemy/orm/session.py", line 1490, in _save_or_update_state
        self._save_or_update_impl(state)
      File "/var/www/html/myapp/app/myapp/local/lib/python2.7/site-packages/SQLAlchemy-0.9.4-py2.7-linux-i686.egg/sqlalchemy/orm/session.py", line 1744, in _save_or_update_impl
        self._save_impl(state)
      File "/var/www/html/myapp/app/myapp/local/lib/python2.7/site-packages/SQLAlchemy-0.9.4-py2.7-linux-i686.egg/sqlalchemy/orm/session.py", line 1716, in _save_impl
        self._attach(state)
      File "/var/www/html/myapp/app/myapp/local/lib/python2.7/site-packages/SQLAlchemy-0.9.4-py2.7-linux-i686.egg/sqlalchemy/orm/session.py", line 1844, in _attach
        self.dispatch.after_attach(self, state.obj())
      File "/var/www/html/myapp/app/myapp/local/lib/python2.7/site-packages/SQLAlchemy-0.9.4-py2.7-linux-i686.egg/sqlalchemy/event/attr.py", line 257, in __call__
        fn(*args, **kw)
    TypeError: unbound method after_attach() must be called with ZopeTransactionExtension instance as first argument (got Session instance instead)
    


Answer: `DBSession` in your case is a
[scoped_session](http://docs.sqlalchemy.org/en/rel_0_9/orm/session.html#sqlalchemy.orm.scoping.scoped_session)
object. To get the actual session, you must call it (Which triggers the call
to underlying session factory).

    
    
    def main(argv=sys.argv): 
        ...
        DBSession.configure(bind=engine)
        Base.metadata.create_all(engine)
        session = DBSession()
        ....
        session.add(model)
    

