Модифицировать данные объекта либо транзакционно, либо нет (в зависимости от необходимости) - PullRequest
2 голосов
/ 13 февраля 2010

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

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

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.

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

Ответы [ 2 ]

2 голосов
/ 13 февраля 2010

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

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 или оставить его вызывающему.

1 голос
/ 13 февраля 2010

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...