我在我的 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 自定义 'save' 和 'delete' 方法来完成,检查他们的 StudyGroup 是否被任何其他 Student 引用,如果没有被引用则删除它。或者更优雅地使用信号。但感觉应该有一种更简单的方法来做到这一点 - 就像 on_delete=models.CASCADE
的逆运算。
有没有办法告诉数据库自动执行此操作?还是我需要编写自定义代码?
最佳答案
您可以使用以下查询删除不再被 Student
引用的 StudyGroup
对象:
StudyGroup.objects.<b>filter(students__isnull=True).delete()</b>
(鉴于您的 related_name=
parameter [Django-doc] 的 ForeignKey
[Django-doc] 设置为 'students'
,因为这是反向关系的名称)。
根据数据库后端,您可以实现一个可以执行特定操作的触发器,例如,当您删除/更新Student
记录时。但这是特定于后端的。
我们可以在Student
模型中添加一个触发器,在删除或保存Student时移除没有
:Student
的StudyGroup
# 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
但是有一些方法可以通过 ORM 绕过 Django 信号。例如使用 QuerySet.update
[Django-doc] .
因此,定期运行该方法可能很有用,例如每天/每小时。我们可以使用 celery
for that [realpython]或 django-periodically
[GitHub] .
话虽这么说,但删除 StudyGroup
本身可能并不是最有必要的。例如,如果您想要检索至少有一个学生的 StudyGroup
的 QuerySet
,我们可以这样写:
# StudyGroups with at least one Student
StudyGroup.objects.filter(<b>student__isnull=False</b>)<b>.distinct()</b>
因此,您可能决定不显示这些 StudyGroup
,而不是删除 StudyGroup
,例如 soft delete [wiktionary] .然后您仍然可以稍后恢复数据,这当然取决于用例。
Note: the
related_name
of aForeignKey
is the name of the relation in reverse, so the name of the attribute of aStudyGroup
to retrieve theQuerySet
ofStudent
s. Therefore naming this'studyGroup'
is a bit "weird". It would also easily result in collisions if there are two or moreForeignKey
s that point toStudyGroup
with the same name.
关于Django:如何删除不再引用的任何外键对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56314944/