Andrey Fedorov
Andrey Fedorov

Reputation: 9669

Is it possible to transactionally mutate GAE entity?

I'd like to have a method like:

class Counter(db.Model):
  n = db.IntegerProperty()

  @db.transactional
  def increment(self):
    entity = db.get(self.key())
    entity.n += 1
    self.n = entity.n
    db.put(entity)

Except in mine, there can be quite a few more properties involved and a bit of logic around which ones get updated when. Following each change to 'entity' with a set on 'self' seems redundant and error-prone.

Can I somehow do this without explicitly updating each self.{prop} for the changed properties?

Iterating through .properties() and .dynamic_properties() comes to mind. Do entities have any other state that might need to be synced?

Upvotes: 2

Views: 139

Answers (2)

Felipe Hoffa
Felipe Hoffa

Reputation: 59175

Either run this method as a @classmethod (no self), or skip the 'get'. Once you have a 'self', there is no need to get.

@staticmethod:

class Counter(db.Model):
  n = db.IntegerProperty()

  @staticmethod
  @db.transactional
  def increment(key):
    entity = db.get(key)
    entity.n += 1
    db.put(entity)

This way you'll avoid having a self and a variable referring to the same entity.

If you still would prefer to do a counter.increment() instead of Counter.increment(counter.key()), another valid approach is starting the transaction in the calling method.

class Counter(db.Model):
  n = db.IntegerProperty()

  def increment(self):
    self.n += 1
    db.put(entity)

# Wherever the code is using Counter.
@db.transactional
def main_method(key):
  entity = db.get(key)
  entity.increment(()

The two main messages I'd like to advice:

  • Avoid using a reference to self and a reference to entity in the same method. You are right when you see that as a 'bad smell' pattern.
  • Consider using ndb instead of db. It's a good evolution of db, and compatible with you current stored objects.

Upvotes: 3

Brent Washburne
Brent Washburne

Reputation: 13158

You have the right approach regarding transactions: https://developers.google.com/appengine/docs/python/datastore/transactions#Uses_for_Transactions

Your question seems to be more about updating a number of entities all at once, and if there are any "hidden" properties. No, there are no hidden properties that need to by synced.

Here is some code on SO from the Python guru himself on how to update multiple entities: Partly update App Engine entity

Upvotes: 2

Related Questions