1. 简介
在Django的models中,一些关系型数据库字段的定义中需要使用on_delete参数,用来决定外键在删除时的行为。这个参数的设置对于数据的完整性有着很大的影响,因此需要特别注意。
2. on_delete参数
在Django的models定义中,对于ForeignKey(外键)字段和OneToOneField字段,都需要定义on_delete参数。常见的值有:
models.CASCADE:级联删除。
models.PROTECT:防止删除,引发ProtectedError错误。
models.SET_NULL:将外键字段设置为null,需要定义null=True。
models.SET_DEFAULT:将外键字段设置为默认值,需要定义default。
models.SET():将外键字段设置为指定的值,需要定义一个函数或者取值对象。
2.1 CASCADE级联删除
CASCADE级联删除是最常用的外键删除方式。在删除主表记录时,Django会自动查找所有依赖于该主表记录的外键表记录,并删除这些依赖表的记录。
from django.db import models
class Author(models.Model):
name = models.CharField(max_length=20)
class Article(models.Model):
title = models.CharField(max_length=100)
author = models.ForeignKey(Author, on_delete=models.CASCADE)
上面的代码中,Article表依赖于Author表的主键,默认情况下删除作者记录时将自动删除文章记录:
author = Author.objects.get(name='张三')
author.delete()
2.2 PROTECT防止删除
PROTECT表示防止删除,有些情况下经常使用,例如关联了支付记录的订单记录,不能随便删除订单,否则会引发麻烦:
class Payment(models.Model):
order = models.ForeignKey(Order, on_delete=models.PROTECT)
如果试图删除一个已经有支付记录的订单,将会抛出ProtectedError错误。
2.3 SET_NULL字段置空
当删除主表记录时,设置外键字段为null。在定义一对一关系时,使用OneToOneField()方法,而不是ForeignKey()方法。因为在一对一关系中,外键字段不能存储null值:
class Place(models.Model):
name = models.CharField(max_length=50)
address = models.CharField(max_length=80)
class Restaurant(models.Model):
place = models.OneToOneField(
Place,
on_delete=models.SET_NULL,
null=True,
blank=True,
)
serves_hot_dogs = models.BooleanField(default=False)
serves_pizza = models.BooleanField(default=False)
p1 = Place(name='The Anchor', address='Trafalgar Square, London')
p1.save()
r = Restaurant(place=p1, serves_hot_dogs=False, serves_pizza=True)
r.save()
p1.delete()
在使用SET_NULL将外键字段置null时,必须同时定义该字段可以为null,否则将会抛出IntegrityError错误。
2.4 SET_DEFAULT字段重置为默认值
当删除主表记录时,将外键字段重置为默认值。
class Car(models.Model):
manufacturer = models.ForeignKey(
Manufactory,
on_delete=models.SET_DEFAULT,
default=1,
)
model_name = models.CharField(max_length=32, null=False, blank=False)
mileage = models.FloatField()
def delete_manufactory(manu_pk):
Manufactory.objects.get(pk=manu_pk).delete()
当调用delete_manufactory()删除默认主键为1的品牌记录时,将自动把所有外键对应的车辆品牌设置为默认品牌。
2.5 SET()字段设置为指定值
当删除主表记录时,将外键字段设置为指定的值。这个指定的值可以是常量、其他字段的值或者自定义的处理函数。
def myfunc():
return Manufactory.objects.create(name='Noname') # 返回新创建的品牌实例
class Car(models.Model):
manufacturer = models.ForeignKey(
Manufactory,
on_delete=models.SET(myfunc),
)
model_name = models.CharField(max_length=32, null=False, blank=False)
mileage = models.FloatField()
def delete_manufactory(manu_pk):
Manufactory.objects.get(pk=manu_pk).delete()
当调用delete_manufactory()删除主键为manu_pk的品牌记录时,将自动调用myfunc()函数创建一个新的品牌,并把所有外键对应的车辆品牌设置为新品牌。
3. 批量操作注意事项
批量操作时要特别注意外键关系的完整性。如果不受管理地删除或增加记录,可能会造成表间关系的不一致性。
例如:执行如下代码来删除所有Article数据,此时删除数据的顺序可能难以确定,因此可能会引起ProtectedError错误:
Article.objects.all().delete()
正确的做法是循环删除所有Article记录:
while Article.objects.count() > 0:
article = Article.objects.first()
article.delete()
4. 总结
修改外键在删除主表记录时的表现行为,对于保证数据完整性十分重要。Django的on_delete参数提供了5种常见外键删除方式,可以根据具体应用场景选择适当的方式。同时,在批量操作时需要特别注意,保证数据记录之间的关系不被破坏。