我在我的 Django Rest Framework 应用程序中嵌套了数据,如下所示:
class Student(models.Model):
studyGroup = models.ForeignKey(StudyGroup, on_delete=models.SET_NULL, blank=True, null=True, related_name='student')
每个学生可能有一个学习小组;一个学生可能没有学习小组。
许多学生可以拥有相同的学习小组。
我想自动删除任何未被任何学生引用的学习组,因为学生已被删除或因为它已更新。
我认为这可以通过为学生自定义"保存"和"删除"方法,检查他们的学习组是否被任何其他学生引用,如果未引用则将其删除来完成。或者通过使用信号可能更优雅。但感觉应该有一种更简单的方法可以做到这一点 - 就像on_delete=models.CASCADE
的倒数一样。
有没有办法告诉数据库自动执行此操作?还是需要编写自定义代码?
您可以使用以下查询删除不再被Student
引用的StudyGroup
对象:
StudyGroup.objects.filter(students__isnull=True).delete()
(这给定了你的ForeignKey
[Django-doc] 的related_name=
参数 [Django-doc] 设置为'students'
,因为这是反向关系的名称)。
根据数据库后端,您可以实现可以执行某些操作的触发器,例如,当您删除/更新Student
记录时。但这是特定于后端的。
我们可以向Student
模型添加一个触发器,以便在删除或保存 s 时删除StudyGroup
Student
而无需Student
:
# app/signals.py
from app.models import Student
from django.db.models.signals importpost_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
模块:
# app/app.py
from django.apps import AppConfig
class MyAppConfig(AppConfig):
# ...
def ready(self):
import app.signals
但是有一些方法可以通过ORM绕过Django信号。通过使用QuerySet.update
[Django-doc]进行充分说明。
因此,定期运行该方法(例如每天/每小时)可能很有用。我们可以将celery
用于[realpython]或django-periodically
[GitHub]。
话虽如此,无论如何,删除StudyGroup
本身可能并不是最必要的。例如,如果您想检索至少有一个学生的StudyGroup
QuerySet
,我们可以这样写:
# StudyGroups with at least one Student
StudyGroup.objects.filter(student__isnull=False).distinct()
因此,与其删除StudyGroup
s,不如决定不显示这些StudyGroup
s,就像软删除[维基词典]一样。然后,您仍然可以稍后恢复数据,这当然取决于用例。
注意:
ForeignKey
的related_name
是关系的反向名称,因此检索Student
sQuerySet
的StudyGroup
的属性名称。因此,命名这个'studyGroup'
有点">奇怪"。如果有两个或多个ForeignKey
指向具有相同名称StudyGroup
,也很容易导致冲突。