views:

345

answers:

3

This is really a python language question, but its wrapped around a Google appengine specific problem.

We have

class User( db.Model ) :
  email = db.StringProperty()
  name = db.StringProperty()
  password = db.StringProperty()
  # more fields..

Because the user account is accessed so often, we keep a copy in session using gaeutilities (as a bonus question, is this bad on GAE? I thought I'd relieve the db a bit.)

class UpdateProfile( webapp.RequestHandler ):
  def post( self ):
    # User posting update to his name
    self.session = sessions.Session()

    #######
    # way#1: update the copy of the User object in SESSION, then .put() it
    self.session[ 'current_user' ].name = self.request.get( 'name' )
    self.session[ 'current_user' ].put()
    # does not work.
    #######

    #######
    # way#2: make a copy of the User object in SESSION, then .put() it
    user = self.session[ 'current_user' ]
    user.name = self.request.get( 'name' )
    user.put()
    # works to update the datastore, but the copy of user in self.session
    # is NOT UPDATED!  I thought Python was
    # pass-by-reference.  It is when you work with lists anyway.
    # Why isn't it "acting right" with this type of object??
    #######


    #######
    # way#3: way that works.
    user = self.session[ 'current_user' ]
    user.name = self.request.get( 'name' )
    user.put()
    self.session[ 'current_user' ] = user
    # works completely
    #######

What is happening in each of these 3 cases? Why don't cases 1 and 2 work?

+3  A: 

I'm guessing:

Putting objects in the Session means that the objects is serialized ( pickled usually ) and stored somewhere (disk, memory, db). When it is retrieved from the Session, a new object is created from serialized the old state.

  • In the first example each self.session[ 'current_user' ] gives you a new object, one of which you update and the other is saved to the db.
  • In the 2nd you get one object, save it to the DB but not in the session.

Btw, Python does "call by sharing", but that has nothing to do with your problem ;-)

THC4k
I think you're probably right. It's a bit odd that gaeutilities' session support serializes objects as soon as they're set. In any case, it would make more sense to simply store the account to Memcache, rather than use the session support.
Nick Johnson
Memcache (like session) is a place to store data, not objects. If you want to update some data in memcache, you must do it explicitly, so even when session is using memcache you still must assume that the data is serialized. (The google memcache API makes no guarantees about object identity on get(), so you shouldn't assume any)
Nick Bastin
I should clarify my previous comment - to the extent that the API accepts "objects" as input, it stores objects, but you should know that it serializes them in reality, so it's a "data" store under the covers. The next request that tries to get the data out of memcache might theoretically not even be handled by the same server, so you can't assume they share a memory space.
Nick Bastin
I realise that, but what surprises me is that it _immediately_ serializes inputs, rather than doing so at a later stage. This seems inefficient.
Nick Johnson
+2  A: 

I'm the author of gaeutilities. I contacted Nick to talk about this, and he gave me some good ideas. I'll have a solution for this in the final 1.3 release.

Je Bowman
+1  A: 

Sorry, I'm new to this site, and I don't see where to comment on an answer?

Anyway, I've fixed the specific issue that instigated the original post. While serialization of data is still suboptimal, happening on write, I have managed to make sure assigning model entities as items in a session will work as expected. Rather than get into rewriting the serialization (would be a major rewrite), I instead chose to detect when a model was being inserted as a session data item, and set a ReferenceProperty for it. This means that the model objects never have to get the overhead of being serialized at all.

If the entity the ReferenceProperty is associated is deleted, the ReferenceProperty in the session will be deleted when you try to load it as well. This does mean you'll have to catch exceptions such as KeyError that are raised by Session, as a dictionary should. I hope this is intuitive enough, but am open to any comments. I'll check this page again a few times over the next several weeks.

Joe Bowman