Django.db.transaction.TransactionManagementError: невозможно выполнить сохранение другого объекта в модели в рамках транзакции
Не могу найти много информации об этом. Это НЕ происходит в тесте django. Я использую DATABASES = { ATOMIC_REQUESTS: True }
. В методе (в созданном мной mixin), вызываемом представлением, я пытаюсь выполнить что-то вроде этого:
def process_valid(self, view):
old_id = view.object.id
view.object.id = None # need a new instance in db
view.object.save()
old_fac = Entfac.objects.get(id=old_id)
new_fac = view.object
old_dets = Detfac.objects.filter(fk_ent__id__exact = old_fac.id)
new_formset = view.DetFormsetClass(view.request.POST, instance=view.object, save_as_new=True)
if new_formset.is_valid():
new_dets = new_formset.save()
new_fac.fk_cancel = old_fac # need a fk reference to initial fac in new one
old_fac.fk_cancel = new_fac # need a fk reference to new in old fac
# any save() action after this crashes with TransactionManagementError
new_fac.save()
Я не понимаю эту ошибку. Я уже создал & сохранил новый объект в db (когда я установил object.id в None & сохранил его). Почему создание других объектов может создать проблему для последующих сохранений?
Я пробовал не инстанцировать объекты new_dets с помощью Formset, а вместо этого явно определять их:
new_det = Detfac(...)
new_det.save()
Но опять же, любое последующее сохранение после этого приводит к ошибке.
Дополнительная информация:
По существу, у меня есть модель Entfac
и модель Detfac
, которая имеет внешний ключ к Entfac
. Мне нужно создать новую модель Enfac
(отдельную в db), а также соответствующую новую модель Detfac
для новой модели Entfac
. Затем мне нужно изменить некоторые значения в некоторых полях для обоих новых и старых объектов и сохранить все это в db.
Ах. Код выше в порядке.
Но оказалось, что сигналы могут быть плохими. Я забыл, что при сохранении Detfac,
возникает сигнал, который идет в другой класс и который в зависимости от обстоятельств добавляет запись в другую таблицу (что-то вроде таблицы истории).
Поскольку этот сигнал - всего лишь одна операция. Что-то вроде этого:
@receiver(post_save, sender=Detfac)
def quantity_adjust_detfac(sender, **kwargs):
try:
detfac_qty = kwargs["instance"].qte
product = kwargs["instance"].fk_produit
if kwargs["created"]:
initial = {# bunch of values}
adjustment = HistoQuantity(**initial)
adjustment.save()
else:
except TypeError as ex:
logger.error(f"....")
except AttributeError as ex:
logger.error(f"....")
Сам по себе тот факт, что ЭТО не было помечено как атомарное, не является проблематичным. НО если одно из этих исключений вылетает, ТО я получаю ошибку управления транзакциями. Я до сих пор не уверен на 100% почему, но в документации django упоминается, что если обернуть все представление в atomic (или любой кусок кода, если на то пошло), то try/except внутри этого блока может дать неожиданный результат, потому что DJango полагается на исключение, чтобы решить, фиксировать транзакцию в целом или нет. А данные, с которыми я тестировал, действительно вызвали исключение (ошибка типа при создании объекта HistoQuantity).
Однако обертывание попытки/исключения менеджером transaction.atomic сработало. Полагая, что это... удаляет/обрабатывает бросок, таким образом, внешний atomic может работать.