Django 模型外键 on_delete=null 但外键还是被删除了

Posted

技术标签:

【中文标题】Django 模型外键 on_delete=null 但外键还是被删除了【英文标题】:Django model foreign key on_delete=null but foreign key is deleted anyway 【发布时间】:2020-06-18 12:20:55 【问题描述】:

我正在尝试将删除时的外键引用从级联更改为 null,如下所示:

created_by = models.ForeignKey(CustomUser,on_delete=models.SET_NULL, null=True)

当我通过从 CustomUser 中删除记录来测试这一点时,我会在执行删除之前和之后获得 Games 表中对象的计数。

p = Games.objects.count()
print(p) #outputs 100
print(Games.objects.filter(created_by=2)) #outputs <QuerySet [<Games: 181 79 None>]>

cu = CustomUser.objects.get(pk=2)
cu.delete()

p = Games.objects.count()
print(p) #outputs 98
print(Games.objects.filter(created_by=2)) # outputs <QuerySet []>

我尝试使用其他值进行删除,但唯一不从数据库中删除行的值是保护。相反,它会引发保护错误

 raise ProtectedError(
django.db.models.deletion.ProtectedError: ("Cannot delete some instances of model 'CustomUser' because they are referenced through a protected foreign key: 'Games.created_by'", <QuerySet [<Games: 181 79 None>]>)

CustomUser 和 Games 模型的代码

class CustomUser(AbstractBaseUser, PermissionsMixin):
    id = models.BigAutoField(primary_key=True)
    username = models.CharField(max_length=15, unique=True)
    email = models.EmailField(max_length=128, unique=True)
    password = models.CharField(max_length=254)
    salt = models.CharField(max_length=128)
    email_verified = models.BooleanField(default=False)
    avatar = models.ImageField(blank=True, null=True)
    date_joined = models.DateTimeField(default=timezone.now)
    last_updated = models.DateTimeField(blank=True, null=True)
    is_active = models.BooleanField(default=False)
    is_staff = models.BooleanField(default=False)

    USERNAME_FIELD = 'email'
    objects = CustomUserManager()

    def __str__(self):
        return self.email



class Games(models.Model):
    CATEGORY = (
        ('general knowledge','general knowledge'),
        ('science','science'),
        ('mythology','mythology'),
        ('sports','sports'),
        ('geography','geography'),
        ('history','history'),
        ('politics','politics'),
        ('art','art'),
        ('celebrities','celebrities'),
        ('animals','animals'),
        ('vehicles','vehicles'),
        ('entertainment','entertainment'),
        ('random','random')
    )

    id = models.BigAutoField(primary_key=True)
    number_of_questions = models.SmallIntegerField()
    number_of_players = models.SmallIntegerField()
    start_time = models.DateTimeField(default=timezone.now)
    category = models.CharField(max_length=18, choices=CATEGORY)
    created_by = models.ForeignKey(CustomUser,on_delete=models.SET_NULL, null=True)
    result_id = models.ForeignKey(Results, on_delete=models.CASCADE, null=True, blank=True)
    sudden_death_id = models.ForeignKey(SuddenDeath, on_delete=models.CASCADE, null=True, blank=True)

    objects = models.Manager()

    def __str__(self):
        return '%s %s %s' % (self.id, self.result_id, self.sudden_death_id)

【问题讨论】:

你的 Django 版本是多少? @LucasWieloch 3.0.7 models.SET_NULL 的外键参数是正确的。您确定您的迁移是最新的吗? @LucasWieloch 是的,我只是仔细检查了一下,我的迁移是最新的 好吧,我会提示您在删除之前通过将游戏 pks 连接到cu 来测试它,并在删除后执行Games.objects.filter(pk__in=game_pks) 以确保记录仍然存在。除此之外,我建议您发布一个带有更大代码 sn-p 的示例,因为无论错误是什么,它都可能超出您问题中块的范围 【参考方案1】:

我使用 django-extensions reset_db 重置了我的数据库,删除了所有迁移并重新运行。 makemigrations & migrate 从我的灯具中导入相同的数据集后,我再次运行了这段代码

        p = Games.objects.count()
        print(p) #output 100
        print(Games.objects.filter(created_by=2)) # output <QuerySet [<Games: 181 79 None>]>
        cu = CustomUser.objects.get(pk=2)
        cu.delete()
        p = Games.objects.count()
        print(p) #output 100
        print(Games.objects.get(pk=181)) #output 181 79 None

这确认外键行没有被删除。

我将此标记为已接受的答案,但如果有人能够解释如何在不重置数据库的情况下解决问题,我会将其标记为已接受的答案。

【讨论】:

以上是关于Django 模型外键 on_delete=null 但外键还是被删除了的主要内容,如果未能解决你的问题,请参考以下文章

django关系类型字段

为啥我的模型的“on_delete=models.CASCADE”不生成级联外键约束?

如何使用 on_delete 属性在用户模型的外键字段中设置用户全名?

django数据模型on_delete, db_constraint的使用

Django - 删除外键时删除数据库条目

django模型在具有外键的字段中没有列错误