Как commit_on_success обрабатывает вложенность?

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

У меня есть код, который сводится к следующему:

from django.db import transaction

@transaction.commit_on_success
def process_post():
    #do stuff with database
    for reply in post_replies:
        process_post_reply(reply)

@transaction.commit_on_success
def process_post_reply(reply):
    #do stuff with database

Я хочу знать, что произойдет, если сбой process_post_reply().

Как commit_on_success обрабатывает вложенность? Понятно ли это, чтобы зафиксировать каждый process_post_reply(), или если кто-то провалит все кавычки process_post()?

Ответ 1

Вот исходный код: https://github.com/django/django/blob/1.2.4/django/db/transaction.py#L286

И enter_transaction_management так же просто, как перенос нового режима обработки транзакций в стек потока.

Итак, в вашем случае, если process_post_reply() завершается сбой (например, исключение), тогда транзакция полностью откатывается, а затем исключение распространяется и вверх от process_post(), но отката назад.

И нет, если один process_post_reply() терпит неудачу, тогда весь process_post() не откат - там нет волшебства, только COMMIT и ROLLBACK на уровне базы данных, а это означает, что то, что возвращается, - это только то, что было записанный в БД после последнего зарегистрированного process_post_reply().

Подводя итоги, Я думаю, что вам нужно всего лишь один commit_on_success() вокруг process_post, возможно, поддерживаемый точки сохранения транзакций - которые, к сожалению, доступны только в бэкэнд PostgreSQL, хотя MySQL 5.x также поддерживает их.

EDIT 10 Apr 2012: поддержка Savepoint для MySQL теперь доступна в Django 1.4

EDIT 2 июля 2014 г.: управление транзакциями полностью переписано в Django 1.6 - https://docs.djangoproject.com/en/1.6/topics/db/transactions/ и commit_on_success устарел.

Ответ 2

Чтобы получить больше контроля над управлением транзакциями, полезно использовать transaction.commit_manually():

@transaction.commit_on_success
def process_post(reply):
    do_stuff_with_database()
    for reply in post_replies:
        process_post_reply(transaction_commit_on_success=False)

def process_post_reply(reply, **kwargs):
    if kwargs.get('transaction_commit_on_success', True):
        with transaction.commit_manually():
            try:
                do_stuff_with_database()
            except Exception, e:
                transaction.rollback()
                raise e
            else:
                transaction.commit()
    else:
        do_stuff_with_database()

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