Почему я все еще получаю 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 вещи:

  1. Во время обновления он пытался получить образец, который был в процессе удаления, что вызвало исключение.
  2. Удаление записи образца в конечном итоге не удалось, потому что есть 2 ссылки на запись, которые пытались управлять тем, как она должна удаляться (MSRun.sample был установлен на RESTRICT и Animal.last_serum_sample был установлен на CASCADE). И один выполнял сохранение во время операции удаления другого.
  3. .

Итак, я изменил 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.

Вернуться на верх