Django models’ foreign keys require you to set an
on_delete function. This is true for
ManyToMany fields. In this blog post, I will go into detail on what each method does and illustrate when to apply each one.
First and foremost, It is important to understand that the foreign key
on_delete handler only works for the related entity, not the source entity:
class Post(models.Model): title = models.CharField(max_length=255) class Comment(models.Model): content = models.TextField() #👇🏻 post = models.ForeignKey(Post, on_delete=[?])
In the example above,
Comment will be the target of any
on_delete handler when you delete
Post, but not the other way around. If you delete a
Comment instance, nothing will happen to
Now that we understand that, let’s get into each of the methods!
Let’s take our
Comment example and set
class Comment(models.Model): ... # 👇🏻 post = models.ForeignKey(Post, on_delete=models.CASCADE)
When we call post.delete(), it will cascade down to the foreign key relationship(s). Meaning that both Post and Comment get deleted.
models.SET(function or value)
The first of the 3 in the
models.SET[...] family. For this method,
Comment relationships don’t really make sense. You wouldn’t set the foreign key field of a model to a different post when you delete the post.
A more realistic example would be to have a
ForeignKey from a
Comment to a user. When we delete the user (looking at you, GDPR), we may want to keep their comments. You’ve probably seen this on Reddit
Something similar would be achieved by using the
def get_deleted_user_instance(): return User.objects.get(username='deleted') class Comment(models.Model): ... author = models.ForeignKey(User, on_delete=models.SET(get_deleted_user_instance)) # 👆🏻
Now if we were to delete the user instance, it would be replaced by the “deleted” user instance.
SET function from a few seconds ago?
SET_DEFAULT sets the foreign key it its default value as defined in the model.
ANONYMOUS_USER_ID = 1 class Comment(models.Model): ... author = models.ForeignKey(User, default=ANONYMOUS_USER_ID, on_delete=models.SET_DEFAULT) # 👆🏻
Now that we understand the
SET family, this one isn’t very hard to grasp.
It is important to note that you can only add
SET_NULL if your model has
null=True as one of its properties.
Comment relationship is very realistic here. Protect throws an exception when you try to delete the related model:
class Comment(models.Model): ... # 👇🏻 post = models.ForeignKey(Post, on_delete=models.PROTECT)
If we create a
Post, you can still delete it. However, once someone has left a
Comment, deleting will be blocked and Django will throw a
As the method name suggests, this will do nothing. The implementation is just a
# in django.db.models.deletion def DO_NOTHING(collector, field, sub_objs, using): pass
This can be pretty dangerous as your model could be pointing at a non-existent entity. You should generally avoid this unless you have relationships managed on a database level (e.g. your database will make sure your foreign keys have integrity).
That’s a wrap!
Hopefully you learned a thing or two from this. Most of what you read here is also written in the official Django documentation but without detailed examples.
If you want to go even deeper (and I’d highly recommend doing so) you should read the Django on_delete handlers source code.