Изменение данных сущности транзакционно или нет (в зависимости от необходимости)

Каков наилучший способ сохранить код модульным и развязанным, но не вводить транзакцию дважды?

У организаций часто есть методы класса для загрузки, изменения и хранения данных. Часто это должно быть транзакционным, чтобы оно соответствовало объектам child / sibling / cousin. Вот шаблон:

class MyEntity(db.Model): # ... some properties @classmethod def update_count(cls, my_key): def txn(): me = db.get(my_key) me.count += 23 me.put() OtherEntity.update_descendants(ancestor=me) return db.run_in_transaction(txn) 

Обычно вы должны извлекать сущности, изменять их и хранить их в блоке. Эта техника более эффективна; но иногда производительность менее важна, чем модульность и ремонтопригодность. Два обновления должны быть развязаны. (Возможно, update_descendants вызывается часто изолированно и отвечает за хранение данных.)

Но следующий код является ошибкой:

 class OtherEntity(db.Model): # ... some properties @classmethod def update_descendants(cls, ancestor): def txn(): # XXX Bug! descendants = cls.all().ancestor(ancestor).fetch(10) for descendant in descendants: descendant.update_yourself(ancestor.count) db.put(descendants) return db.run_in_transaction(txn) 

Это вызывает исключение:

 >>> MyEntity.update_count(my_key=some_key_i_have) Traceback (most recent call last): ... BadRequestError: Nested transactions are not supported. 

Итак, как я могу получить лучшее из обоих миров: модульность и правильность?

Я использую шаблон, чтобы иметь параметр, указывающий, требуется ли транзакционное поведение.

 class OtherEntity(db.Model): # ... some properties @classmethod def update_descendants(cls, ancestor, with_transaction=True): if with_transaction: return db.run_in_transaction(cls.update_descendants, ancestor, with_transaction=False) # Now I can assume I am in a transaction one way or another... descendants = cls.all().ancestor(ancestor).fetch(10) for descendant in descendants: descendant.update_yourself(ancestor.count) return db.put(descendants) 

Тот же принцип можно было бы расширить, чтобы указать, следует ли брать на себя ответственность за put или оставить его вызывающему.

Я бы предложил сделать функции транзакций методами класса верхнего уровня. Затем вы можете вызвать их напрямую или с помощью db.run_in_transaction , если это необходимо.