django.db.транзакция.TransactionManagementError: не удается сохранить другой объект в модели в транзакции

#django #django-orm

Вопрос:

Не могу найти много информации об этом. Это происходит НЕ в тесте джанго. Я использую 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()  

Я не понимаю этой ошибки. Я уже создал и сохранил новый объект в БД (когда я установил object.id ни к чему и сохранил это). Почему создание других объектов создает проблему для дальнейшего сохранения?

Я попытался не создавать экземпляры объектов new_dets с помощью набора форм, а вместо этого явно определить их:

 new_det = Detfac(...) new_det.save()  

Но опять же, любое дальнейшее сохранение после этого вызывает ошибку.

Более подробная информация:

По сути, у меня есть Entfac модель, и Detfac модель, к Entfac которой есть внешний ключ . Мне нужно создать экземпляр нового Enfac (отличный в бд), а также соответствующий новый Detfac для нового Entfac . Затем мне нужно изменить некоторые значения в некоторых полях как для новых, так и для старых объектов, и сохранить все это в бд.

Ответ №1:

Ах. Приведенный выше код в порядке.

Но, оказывается, сигналы могут быть плохими. Я забыл, что при сохранении 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"....")  

Сам по себе тот факт, что ЭТО не было помечено как атомарное, не является проблемой. НО если одно из этих исключений вызывает исключение, я получаю транзакцию transactionmanagementerror. Я до сих пор не уверен на 100%, почему, но в документах django упоминается, что при обертывании всего представления в atomic (или любого фрагмента кода, если на то пошло), попытка/исключение внутри этого блока может привести к неожиданному результату, потому что DJango полагается на исключение, чтобы решить, следует ли фиксировать транзакцию в целом. И данные, с которыми я тестировал, фактически вызвали исключение (ошибка типа при создании объекта HistoQuantity).

Однако обертывание попытки/исключения транзакцией.атомарный менеджер работал. Догадываюсь, что это… удалил/обработал бросок, таким образом, внешний атом мог работать.