InvalidRequestError, SQLAlchemy question

54 views
Skip to first unread message

Adam Morris

unread,
Feb 27, 2013, 11:14:50 PM2/27/13
to mediacore-...@googlegroups.com
Hi there on my development version of mediacore I think I must have tried adding an already existing user or something that never found its way into the database, because at the line:

DBSession.add(user)    # user created with user = User() and then fields filled out programmatically

I get the following error:

Object '<User at 0x110ecbd10>' is already attached to session '1' (this is '9')

This user isn't in my database so I'm not sure what I'm supposed to do. Any ideas? 

Adam Morris

unread,
Feb 28, 2013, 5:51:26 AM2/28/13
to mediacore-...@googlegroups.com
This is the code:


                user = User()
                user.display_name = username
                user.user_name = username
                user.email_address = "{}{}".format(user.user_name, auth_to_use.default_domain())
                user.password = u'uselesspassword#%^^#@'
                user.groups = auth_to_use.default_groups()

                try:
                    #actually add the user
                    DBSession.add(user)
                    DBSession.commit()
                except IntegrityError, e:
                    DBSession.rollback()
                    return None
                except InvalidRequestError, e:
                    print("What do I do here??")
                except Exception, e:
                    DBSession.rollback()
                    return None

Felix Schwarz

unread,
Feb 28, 2013, 4:16:20 PM2/28/13
to mediacore-...@googlegroups.com

looks strange. Is it possible for you to reproduce the problem in an isolated
piece of code (e.g. a unit test)? What happens if you don't add the user to
the session? Is it still persisted after the request?

fs


Adam Morris

unread,
Feb 28, 2013, 8:03:04 PM2/28/13
to mediacore-...@googlegroups.com
Reading the sqlalchemy docs says that using DBSession.merge is the ticket. That does solve the problem, but I don't understand why that user would be in a different session. In fact, I'm thinking specifically it SHOULD NOT be.

This code is run in MediaCoreAuthenticatorPlugin.authenticate. I've modded the code as below and it works now. My environment authenticates to ldap for staff and imap for students, which I know from parsing their username:

    from pylons import config as pylonsconfig

    def authenticate(self, environ, identity, notagain=False):
        login = super(MediaCoreAuthenticatorPlugin, self).authenticate(environ, identity)
        if login is None:
            if notagain:
                return None   # prevent infinite loop

            username = identity['login']
            password = identity['password']

            if re.match(r'^[a-z]+[0-9]{2}$', username):
                auth_to_use = pylonsconfig['imap']
            else:
                auth_to_use = pylonsconfig['ldap']
                
            if not auth_to_use.auth(username, password):
                return None
            else:
                # Use the model to create the user which automagically gets put in the database
                user = User()
                user.display_name = username
                user.user_name = username
                user.email_address = "{}@{}".format(user.user_name, auth_to_use.default_domain())
                user.password = u'uselesspassword#%^^#@'
                user.groups = auth_to_use.default_groups()

                try:
                    #actually add the user
                    DBSession.add(user)
                    DBSession.commit()
                except IntegrityError, e:
                    DBSession.rollback()
                    return None
                except InvalidRequestError, e:
                    new_user = DBSession.merge(user)
                    DBSession.add(new_user)   # TODO: Another try block
                    DBSession.commit()
                except Exception, e:
                    DBSession.rollback()
                    return None

                # Now repoze.who should be able to login
                return self.authenticate(environ, identity, notagain=True)

        user = self.get_user(login)
        # The return value of this method is used to identify the user later on.
        # As the username can be changed, that's not really secure and may 
        # lead to confusion (user is logged out unexpectedly, best case) or 
        # account take-over (impersonation, worst case).
        # The user ID is considered constant and likely the best choice here.
        return user.user_id

Felix Schwarz

unread,
Mar 12, 2013, 6:48:18 AM3/12/13
to mediacore-...@googlegroups.com
Hey,

I just tried a similar example in my environment and it just works.

In MediaCoreAuthenticatorPlugin.authenticate() I added the user the same way
you did (just with DBSession.add) and it works fine.

I'm not sure why it's different in your install.

One thing I do different is that I import model objects etc always from
'mediacore.model' like this:
from mediacore.model import DBSession
but that shouldn't make any real difference.

You can try to add this to your development.ini
[app:main]
...
db.check_for_leaked_connections = True

After each request you should see a warning message if there was an extra DB
connection opened.

That feature helped me in the past to find some pretty delicate issues with
multiple DBSessions. Alternatively you could debug the actual DBsession (start
from a middleware) to find out which objects get attached to the session.

fs

Reply all
Reply to author
Forward
0 new messages