2

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?

Dan McGrath
  • 41,220
  • 11
  • 99
  • 130
Andrey Fedorov
  • 9,148
  • 20
  • 67
  • 99
  • Not sure what you mean by not "explicitly updating" the property. Any "logic" will be taken care of when you update the property, surely? Can you give an example of what you want to happen? – Daniel Roseman May 06 '13 at 22:00
  • I have to ask, why on earth are you fetching the entity inside the increment method, if you have `self.key()` then you already have the object. I think you need to think about what you are actually trying to achieve and how you go about it. – Tim Hoffman May 08 '13 at 01:24
  • @TimHoffman I'd like to update it transactionally. – Andrey Fedorov May 08 '13 at 04:19
  • @DanielRoseman: reworded the part which might have been confusing. The method I'm working with is a bit longer, and remembering to update twice for every update seems like it could potentially cause bugs. – Andrey Fedorov May 08 '13 at 04:30

2 Answers2

2

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.
Felipe Hoffa
  • 54,922
  • 16
  • 151
  • 325
1

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

Community
  • 1
  • 1
Brent Washburne
  • 12,904
  • 4
  • 60
  • 82