Django:如何删除不再引用的任何外键对象

时间:2019-05-26 15:36:50

标签: django foreign-keys

我在Django Rest Framework应用中嵌套了数据,如下所示:

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

每个学生都有一个学习小组;一个学生可能没有学习小组。

许多学生可以有相同的学习小组。

我想自动删除任何未被任何学生引用的StudyGroup,因为该学生已被删除或已被更新。

我认为可以通过为Student自定义“保存”和“删除”方法,检查其他学生是否引用了他们的StudyGroup,然后删除未引用的StudyGroup来完成。也许通过使用信号更优雅。但这似乎应该有一种更简单的方法来实现–像on_delete=models.CASCADE的反函数。

是否可以告诉数据库自动执行此操作?还是我需要编写自定义代码?

1 个答案:

答案 0 :(得分:1)

您可以使用以下查询来删除StudyGroup不再引用的Student对象:

StudyGroup.objects.filter(students__isnull=True).delete()

(鉴于您related_name= parameter [Django-doc]ForeignKey [Django-doc]设置为'students',因为这是相反关系的名称。)

取决于数据库后端,您可以实现可以执行某些操作的 trigger ,例如,当您删除/更新Student记录时。但这是特定于后端的。

我们可以向Student模型添加触发器,以在删除或保存StudyGroup s时删除没有Student的{​​{1}} s:

Student

您将需要在应用程序配置中导入# app/signals.py from app.models import Student from django.db.models.signals import post_delete, post_save 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

但是有一些方法可以通过ORM绕过Django信号。例如,使用QuerySet.update [Django-doc]

因此,定期运行该方法可能很有用,例如每天/每小时。我们可以使用celery for that [realpython]django-periodically [GitHub]

话虽如此,但实际上删除# app/app.py from django.apps import AppConfig class MyAppConfig(AppConfig): # ... def ready(self): import app.signals并不是最必要的。例如,如果您想检索至少有一个学生的StudyGroup个中的QuerySet个,我们可以这样写:

StudyGroup

因此,您可能决定不显示这些# StudyGroups with at least one Student StudyGroup.objects.filter(student__isnull=False).distinct(),而不是像soft delete [wiktionary]那样{em}删除StudyGroup。然后您仍然可以稍后恢复数据,这当然取决于用例。

  

注意StudyGroup的{​​{1}}是反向关系的名称,因此related_name的属性名称用于检索ForeignKey个中的StudyGroup个。因此,命名此QuerySet有点“ 很奇怪”。如果有两个或多个指向同名的Student的{​​{1}},则很容易导致冲突