zopefoundation / zope.sqlalchemy

Integration of SQLAlchemy with transaction management
Other
32 stars 34 forks source link

AttributeError: 'NoneType' object has no attribute '_iterate_parents' #11

Closed mindbrave closed 10 years ago

mindbrave commented 10 years ago

I get this error rarely, but it happens. I have no idea how to debug it.

databaselayer/blog.py", line 449, in set_notifications_as_read
).limit(limit).update({'is_read': True})
local/lib/python2.7/site-packages/sqlalchemy/orm/query.py", line 2735, in update
update_op.exec_()
local/lib/python2.7/site-packages/sqlalchemy/orm/persistence.py", line 868, in exec_
self._do_post()
local/lib/python2.7/site-packages/sqlalchemy/orm/persistence.py", line 968, in _do_post
session.dispatch.after_bulk_update(self)
local/lib/python2.7/site-packages/sqlalchemy/event/attr.py", line 257, in __call__
fn(*args, **kw)
local/lib/python2.7/site-packages/sqlalchemy/event/legacy.py", line 36, in wrap_leg
return fn(*conv(*args))
local/lib/python2.7/site-packages/zope.sqlalchemy-0.7.4-py2.7.egg/zope/sqlalchemy/datamanager.py", line 237, in after_bulk_update
mark_changed(session, self.transaction_manager, self.keep_session)
local/lib/python2.7/site-packages/zope.sqlalchemy-0.7.4-py2.7.egg/zope/sqlalchemy/datamanager.py", line 210, in mark_changed
join_transaction(session, STATUS_CHANGED, transaction_manager, keep_session)
local/lib/python2.7/site-packages/zope.sqlalchemy-0.7.4-py2.7.egg/zope/sqlalchemy/datamanager.py", line 202, in join_transaction
DataManager(session, initial_state, transaction_manager, keep_session=keep_session)
local/lib/python2.7/site-packages/zope.sqlalch
local/lib/python2.7/site-packages/zope.sqlalchemy-0.7.4-   py2.7.egg/zope/sqlalchemy/datamanager.py", line 65, in __init__
self.tx = session.transaction._iterate_parents()[-1]
AttributeError: 'NoneType' object has no attribute '_iterate_parents'
lrowe commented 10 years ago

This error indicates that session.transaction is None, which means that the session transaction has not been begun. That would happen if session.autocommit is True, zope.sqlalchemy isn't applicable for that case. http://docs.sqlalchemy.org/en/rel_0_9/orm/session.html#autocommit-mode

mindbrave commented 10 years ago

Autocommit is disabled (at least I didn't set it myself). SQLAlchemy==0.9.3 zope.sqlalchemy==0.7.4

Functions (it's called in most of the pyramid views):

    def get_user_notifications(self, user_id):
        notifications_data = self.blog_repository.get_user_notifications(
            user_id, limit=30
        )
        self.set_notifications_as_read(user_id)
        return notifications_data

    def set_notifications_as_read(self, user_id, limit=None):
        notifications = self.session.query(Notification).filter(
            Notification.user_id == user_id
        ).limit(limit).update({'is_read': True})

Sessionmaker conf:

import sqlalchemy
from sqlalchemy.orm import sessionmaker, scoped_session
from zope.sqlalchemy import ZopeTransactionExtension

Session = scoped_session(sessionmaker(
     extension=ZopeTransactionExtension()
))

def includeme(config):
     """ Setup sqlalchemy session connection for pyramid app.

     :param config: Pyramid configuration

     """
    config.include('pyramid_tm')
    # creates database engine from ini settings passed to pyramid wsgi
    engine = sqlalchemy.engine_from_config(
        config.get_settings(), connect_args={
             'charset': 'utf8'
        }
     )
    # scoped session gives us thread safe session
    Session.configure(bind=engine)
    # make database session available in every request
    config.add_request_method(
       callable=lambda request: Session, name='dbsession', property=True
    )
lrowe commented 10 years ago

My best bet is that somehow you are using an old session object. Could instances of your class that sets sets self.session somehow remain between requests?

Anyway, closing as this is impossible to reproduce.

mindbrave commented 10 years ago

I'm creating new repository instance in every view and that repository gets request.dbsession as constructor parameter, so self.session is a session retrieved from request.dbsession which means that it's a call to Session. It's happening only on local test environment, didn't happen on production yet.

cjw296 commented 7 years ago

I've seen this too, but only when the mapper is incorrectly configured.