Почему я все еще получаю RestrictedError в Django при удалении связанных записей?
У меня есть следующий код в модульном тесте:
serum_sample.msruns.all().delete()
print(f"THIS SHOULD BE 0: {serum_sample.msruns.count()}")
serum_sample.delete()
Я выхожу:
THIS SHOULD BE 0: 0
В коде я удалил связанные записи, которые имеют отношения RESTRICT, но я все равно получаю эту ошибку:
django.db.models.deletion.RestrictedError: ("Cannot delete some instances of model 'Sample' because they are referenced through restricted foreign keys: 'MSRun.sample'.", {<MSRun: MS run of sample BAT-xz971 with Default by anonymous on 2021-04-29>, <MSRun: MS ...
Отношение:
class MSRun(HierCachedModel, MaintainedModel):
...
sample = models.ForeignKey(
to="DataRepo.Sample",
on_delete=models.RESTRICT,
related_name="msruns",
help_text="The sample that was run on the mass spectrometer.",
)
Я думал, что для удаления образца достаточно удалить записи MSRun, которые на него ссылаются...
Почему бы это не было так?
OK. Я разобрался, хотя до сих пор не могу объяснить содержание сообщения об ошибке. Конечно, я не предоставил достаточно информации, чтобы кто-то другой смог ответить на вопрос, потому что я не мог определить источник проблемы. Оказалось, что это были 2 внешних ключа между таблицами Animal и Sample:
class Animal(MaintainedModel, HierCachedModel, ElementLabel):
...
last_serum_sample = models.ForeignKey(
to="DataRepo.Sample",
null=True,
blank=True,
on_delete=models.CASCADE,
related_name="animals",
)
class Sample(MaintainedModel, HierCachedModel):
...
animal = models.ForeignKey(
to="DataRepo.Animal",
on_delete=models.CASCADE,
null=False,
related_name="samples",
)
class MSRun(HierCachedModel, MaintainedModel):
...
sample = models.ForeignKey(
to="DataRepo.Sample",
on_delete=models.RESTRICT,
related_name="msruns",
)
Ранее Animal.last_serum_sample
был @cached_function
/@property
, но в моей текущей ветке я изменил его на @maintained_field_function
с именем _last_serum_sample()
, который выдает значение для обновления нового поля модели last_serum_sample
, которое при вызове .save()
или .delete
записи (или иерархически связанной записи) будет использовать вывод ``. Animal.save()
вызывается для обновления нового _last_serum_sample()
для обновления last_serum_sample
. Но его старое значение было образцом, который был удален. Таким образом, происходило 2 вещи:
- Во время обновления он пытался получить образец, который был в процессе удаления, что вызвало исключение.
- Удаление записи образца в конечном итоге не удалось, потому что есть 2 ссылки на запись, которые пытались управлять тем, как она должна удаляться (MSRun.sample был установлен на RESTRICT и Animal.last_serum_sample был установлен на CASCADE). И один выполнял сохранение во время операции удаления другого. .
Итак, я изменил Animal.last_serum_sample.on_delete
на SET_NULL
вместо CASCADE
:
class Animal(MaintainedModel, HierCachedModel, ElementLabel):
...
last_serum_sample = models.ForeignKey(
to="DataRepo.Sample",
null=True,
blank=True,
on_delete=models.SET_NULL,
related_name="animals",
)
и теперь мой тест проходит.
Я также добавил перехват исключения при попытке доступа к внешнему ключу удаленной записи.
Я все еще нахожусь в процессе отладки, так что, возможно, мне придется еще многое решить, но я был весьма доволен тем, что разобрался с противоречием.
Теперь я задаюсь вопросом, почему проблема представлена в виде RestrictedError, который ссылается на ссылки на эту запись образца из записей MSRun, которые никак не связаны с этим образцом и/или животным (отношения иерархические: Animal->Sample->MSRun
).
Независимо от этого, кажется, что запуск автообновления связанных записей может привести к ошибкам, когда несколько различных записей модели имеют конфликтующие поведения on_delete.