pyodbc Транзакции в Django View фиксируют изменения, несмотря на попытки отката

Вопрос или проблема

Я работаю над приложением Django, в котором использую pyodbc для подключения к базе данных SQL Server на AWS RDS. Мне нужно выполнить серию сырых SQL-запросов (включая INSERT, UPDATE, DELETE, DISABLE или ENABLE триггер и т. д.) в рамках транзакции, гарантируя, что никакие изменения не будут зафиксированы, если произойдет ошибка во время выполнения. Однако я столкнулся с проблемой, когда изменения фиксируются в базе данных, даже если возникает ошибка, и я вызываю rollback().

Вот что я попробовал до сих пор:

Установил autocommit = False на соединении pyodbc, чтобы предотвратить автоматические фиксирования.
Использовал несколько курсоров для разных SQL-запросов, все под одним соединением с autocommit = False.
Обернул представление Django с помощью @transaction.non_atomic_requests, чтобы отключить стандартное управление транзакциями Django.
Также пытался обернуть код в transaction.atomic(), чтобы посмотреть, может ли управление транзакциями Django помочь.
Несмотря на все эти усилия, изменения по-прежнему фиксируются в базе данных после возникновения ошибки в представлении Django, но когда я запускаю точно такой же код в отдельном скрипте Python (вне Django), rollback работает идеально, и никакие изменения не фиксируются.

Вот упрощенная версия кода, который я использую:

import pyodbc
from django.conf import settings
from django.db import transaction

@transaction.non_atomic_requests  # Попробовал это, чтобы отключить оборачивание транзакций в Django
def my_view(request):
    connection = pyodbc.connect(settings.DATABASE_CONNECTION_STRING, autocommit=False)
    try:
        # Начать транзакцию
        cursor1 = connection.cursor()
        cursor1.execute("UPDATE my_table SET column1 = 'value1' WHERE id = 1")

        cursor2 = connection.cursor()
        cursor2.execute("INSERT INTO my_table (column1) VALUES ('value2')")

        # Эта строка вызовет ошибку
        cursor3 = connection.cursor()
        cursor3.execute("INVALID SQL STATEMENT")  # Это должно инициировать откат

        # Зафиксировать, если все запросы успешны
        connection.commit()

    except Exception as e:
        print("Произошла ошибка, откат транзакции:", e)
        connection.rollback()  # Это должно предотвратить фиксацию любых изменений

    finally:
        cursor1.close()
        cursor2.close()
        cursor3.close()
        connection.close()

Краткое описание проблемы:

Когда я запускаю этот код как отдельный скрипт вне Django, он работает как ожидается, без фиксации изменений, если произошла ошибка.
Когда я запускаю тот же код внутри представления Django, изменения фиксируются, даже если произошла ошибка.

Дополнительные заметки:

Я в курсе того, что Django оборачивает представления в транзакции по умолчанию, поэтому я попробовал @transaction.non_atomic_requests, чтобы предотвратить обработку транзакций Django.
Я подтвердил, что autocommit установлен на False на соединении pyodbc.

Вопросы:

1) Есть ли что-то в жизненном цикле запроса Django или промежуточном программном обеспечении, что может мешать моей ручной обработке транзакций?

2) Есть ли способ гарантировать, что rollback() работает правильно в этой настройке?

3) Есть ли какие-либо конкретные настройки или подходы в Django, которые могут предотвратить фиксацию этих изменений?

Я буду признателен за любые рекомендации по этой проблеме. Спасибо!

Ответ или решение

Проблема с транзакциями в Django при использовании pyodbc: непреднамеренное подтверждение изменений

Введение

Ваша проблема заключается в том, что изменения в базе данных фиксируются даже после возникновения ошибки в Django-приложении, когда вы используете библиотеку pyodbc для работы с AWS RDS SQL Server. Это действительно может вызвать замешательство и требует комплексного анализа, так как, несмотря на отказ от автоматических подтверждений, ваше поведение системы не соответствует ожиданиям.

1. Взаимодействие с жизненным циклом запроса в Django

Ваш вопрос о том, может ли что-то в жизненном цикле запроса или промежуточное ПО Django вмешаться в вашу ручную обработку транзакций, имеет смысл. Django применяет механизм управления транзакциями к своим запросам. Даже если вы добавили декоратор @transaction.non_atomic_requests, могут быть осложнения из-за конфигурации и того, как взаимодействует ваш драйвер базы данных.

Решение:

Попробуйте использовать контекстный менеджер transaction.atomic() вместо декоратора @transaction.non_atomic_requests. Это позволит вам управлять транзакциями более последовательно и предотвратить влияние на них других частей приложения. Например:

from django.db import transaction

def my_view(request):
    with transaction.atomic():
        connection = pyodbc.connect(settings.DATABASE_CONNECTION_STRING, autocommit=False)
        try:
            # Ваш код
        except Exception as e:
            # Обработка исключений

2. Гарантия правильной работы rollback()

Чтобы гарантировать, что метод rollback() срабатывает должным образом, вам необходимо убедиться, что вы не теряете контроль над методом транзакций в Django. Вот несколько рекомендаций:

  • Убедитесь, что autocommit отключен. Ваша настройка должно оставаться без изменений: autocommit=False.
  • Используйте только один курсор на соединение. Подобная организация кода поможет избежать неожиданных ошибок. Например, создавайте курсоры один раз и повторно используйте их.

Пример:

connection = pyodbc.connect(settings.DATABASE_CONNECTION_STRING, autocommit=False)
cursor = connection.cursor()

try:
    cursor.execute("UPDATE my_table SET column1 = 'value1' WHERE id = 1")
    cursor.execute("INSERT INTO my_table (column1) VALUES ('value2')")
    cursor.execute("INVALID SQL STATEMENT")
    connection.commit()
except Exception as e:
    print("Error occurred, rolling back transaction:", e)
    connection.rollback()
finally:
    cursor.close()
    connection.close()

3. Защита от непреднамеренного подтверждения изменений

Одним из эффективных способов предотвратить коммит изменений является использование конфигураций, которые конфликтуют с Djangо и pyodbc. Отключение транзакционного управления Django для данного конкретного представления может быть полезным.

Дополнительные рекомендации:

  • Проверка настроек DATABASES: Убедитесь, что у вас корректно настроены OPTIONS для pyodbc, которые могут включать в себя параметры управления транзакциями.
  • Используйте отладку. Добавление отладочного вывода перед коммитом и внутри вашего блока обработки исключений поможет понять, в каком месте происходит ошибка.

Заключение

Проблема с необратимыми изменениями в вашей базе данных, возникающими при использовании pyodbc в Django, достаточно распространена, и, как правило, связана с конфликтами в транзакционном управлении. Следуя вышеизложенным рекомендациям, вы сможете добиться большей предсказуемости и обеспечить корректное управление транзакциями в вашем приложении.

Если у вас остались вопросы или требуется дальнейшая помощь, не стесняйтесь обращаться.

Оцените материал
Добавить комментарий

Капча загружается...