Django: Как удалить любой объект внешнего ключа, на который больше нет ссылок - PullRequest
1 голос
/ 26 мая 2019

У меня есть вложенные данные в приложении Django Rest Framework, что-то вроде этого:

class Student(models.Model):
    studyGroup = models.ForeignKey(StudyGroup, on_delete=models.SET_NULL,  blank=True, null=True, related_name='student')

Каждый студент может иметь учебную группу; у студента может не быть учебной группы.

У многих студентов может быть одна и та же учебная группа.

Я хотел бы автоматически удалить любую группу StudyGroup, на которую не ссылаются какие-либо студенты, либо потому, что студент был удален, либо потому что он был обновлен.

Я думаю, что это можно сделать, настроив методы 'save' и 'delete' для Student, проверив, ссылается ли на их StudyGroup какой-либо другой Student, и удалив его, если на него нет ссылок. Или, возможно, более элегантно, используя сигналы. Но такое чувство, что должен быть более простой способ сделать это - например, обратное значение on_delete=models.CASCADE.

Есть ли способ указать базе данных сделать это автоматически? Или мне нужно написать собственный код?

1 Ответ

1 голос
/ 26 мая 2019

Вы можете удалить StudyGroup объекты, на которые больше не ссылается Student, с помощью следующего запроса:

StudyGroup.objects.<b>filter(students__isnull=True).delete()</b>

(это дает related_name= параметр [Django-doc] вашего ForeignKey [Django-doc] установлен на 'students', поскольку это имя отношения в обратном порядке).

В зависимости от серверной части базы данных можно реализовать триггер , который может выполнять определенные действия, например, при удалении / обновлении записи Student.Но это зависит от серверной части.

Мы можем добавить триггер к модели Student, чтобы удалить StudyGroup s без Student при удалении или сохранении Student s:

# app/signals.py

from app.models import Student
from django.db.models.signals import <b>post_delete, post_save</b>
from django.dispatch import receiver

@receiver([post_delete, post_save], sender=Student)
def update_delete_student(sender, instance, **kwargs):
    StudyGroup.objects.filter(students__isnull=True).delete()

Вам потребуется импортировать модуль signals в конфигурации вашего приложения:

# app/app.py

from django.apps import AppConfig

class MyAppConfig(AppConfig):

    # ...

    def ready(self):
        import app.signals

Но есть способы обойти сигналы Django через ORM.Например, используя QuerySet.update [Django-doc] .

Поэтому может быть полезно периодически запускать метод, например, каждый день / час.Мы можем использовать celery для этого [realpython] или django-periodically [GitHub] .

ТоПри этом, как бы то ни было, само по себе удаление 10 * 10 может быть не самым необходимым.Например, если вы хотите получить QuerySet из StudyGroup s, у которых есть хотя бы один студент, мы можем написать это следующим образом:

# StudyGroups with at least one Student
StudyGroup.objects.filter(<b>student__isnull=False</b>)<b>.distinct()</b>

Таким образом, вместо , удаляя the StudyGroup s, вы можете решить не показывать эти StudyGroup s, как soft delete [wiktionary] .Затем вы все равно сможете восстановить данные позже, это, конечно, зависит от варианта использования.

Примечание : related_name для ForeignKey - это имяотношение обратное, поэтому имя атрибута StudyGroup для извлечения QuerySet из Student s.Поэтому называть это 'studyGroup' немного " странно ".Это также может легко привести к коллизиям , если есть два или более ForeignKey s, которые указывают на StudyGroup с тем же именем.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...