Django on_delete Explained

This article aims to explain one of the fundamental processes of working with databases in Django, deleting. With that, we have to understand the on_delete parameter and its choices.

So what exactly does the on_delete parameter do?

The on_delete parameter allows us to define what will happen to the instanced of the model associated with the current instance we are deleting.

There are several selection options we can choose from.

  • CASCADE
  • PROTECT
  • RESTRICT
  • SET_NULL
  • SET_DEFAULT
  • SET( )
  • DO_NOTHING

Let’s see what each of them does.

Django on_delete CASCADE

CASCADE option deletes any data connected to the instance we are deleting. In other words, it deletes objects containing the foreign key.

What does that mean in practice?

class Author(models.Model):
    name = models.CharField(max_length=512)


class Book(models.Model):
    title = models.CharField(max_length=255)
    author = models.ForeignKey(Author, on_delete=models.CASCADE)

After defining Book and Author models, we have added two Book and two Author instances to the database.

Authors table.

idname
1William Shakespeare
2Miguel de Cervantes

Books table.

idtitleauthor_id
1Romeo and Juliet1
2Don Quixote2

Now, let’s delete our first author.

>>> Author.objects.get(id=1).delete()

(2, {'Book': 1, 'Author': 1})

By the output, we can see that we deleted two model instances.

Let’s see how our database tables look after deletion.

Authors table.

idname
2Miguel de Cervantes

Books table.

idtitleauthor_id
2Don Quixote2

As you can see, all rows connected to the William Shakespeare instance are deleted. This is what on_delete=models.CASCADE does. If the foreign key model instance is deleted, all related model instances are deleted. This can save us a lot of manual code implementation.

WARNING -  be careful with sensitive data.

Django on_delete PROTECT

Self-explanatory, PROTECT doesn’t allow deletion of instance that is still referenced in some other tables. So if we try to delete an instance that is still referenced, deletion is prevented, and Django raises ProtectedError.

Let’s see this in action.

class Author(models.Model):
    name = models.CharField(max_length=512)


class Book(models.Model):
    title = models.CharField(max_length=255)
    author = models.ForeignKey(Author, on_delete=models.PROTECT)

Same as before, we will add two books and two authors to the database.

Authors table.

idname
1William Shakespeare
2Miguel de Cervantes

Books table.

idtitleauthor_id
1Romeo and Juliet1
2Don Quixote2

If we try to delete William Shakespeare, let’s see what happens.

>>> Author.objects.get(id=1).delete()

django.db.models.deletion.ProtectedError: ("Cannot delete some instances of model 'Author' because they are referenced through protected foreign keys: 'Book.author'.", {<Book: Book object (1)>})

All tables data remain the same in the database.

WARNING - you can always manually delete all referenced Books and then Authors, but then you are probably using models.PROTECT option wrong

Django on_delete RESTRICT

RESTRICT works similarly to PROTECT choice, but it raises RestrictedError instead of ProtectedError.

class Author(models.Model):
    name = models.CharField(max_length=512)


class Book(models.Model):
    title = models.CharField(max_length=255)
    author = models.ForeignKey(Author, on_delete=models.RESTRICT)

Authors table.

idname
1William Shakespeare
2Miguel de Cervantes

Books table.

idtitleauthor_id
1Romeo and Juliet1
2Don Quixote2
>>>Author.objects.get(id=1).delete()

django.db.models.deletion.RestrictedError: ("Cannot delete some instances of model 'Author' because they are referenced through restricted foreign keys: 'Book.author'.", {<Book: Book object (1)>})

The database data remains the same.

So what is the difference between PROTECT and RESTRICT?

RESTRICT allows deleting if your instance references another object that is being deleted with CASCADE choice.

For further explanation, check out the official Django documentation.

Django on_delete SET_NULL

SET_NULL choice sets the value of the deleted foreign key to NULL. When we define the model with on_delete = models.SET_NULL we must add the null = True parameter. Otherwise, Django will not create migrations files and will report ERROR.

Book.author: (fields.E320) Field specifies on_delete=SET_NULL, but cannot be null.HINT: Set null=True argument on the field, or change the on_delete rule.

Accordingly, let us define a new model.

class Author(models.Model):
    name = models.CharField(max_length=512)


class Book(models.Model):
    title = models.CharField(max_length=255)
    author = models.ForeignKey(Author, on_delete=models.SET_NULL, null=True)

Database data before deleting any of the authors look like this.

Authors table.

idname
1William Shakespeare
2Miguel de Cervantes

Books table.

idtitleauthor_id
1Romeo and Juliet1
2Don Quixote2

Let’s delete the author with id=1.

>>>Author.objects.get(id=1).delete()

(1, {'Author': 1})

As you can see, the Author is successfully deleted. Let’s look at the database.

Authors table.

idname
2Miguel de Cervantes

Books table.

idtitleauthor_id
1Romeo and Julietnull
2Don Quixote2

As expected, the Author instance is deleted, and the Book is still in the database, but no references to the deleted Author.

That’s why we needed to set null = True.

Django on_delete SET_DEFAULT

SET_DEFAULT choice works similarly to SET_NULL, except it sets the default value when a foreign key is deleted. If we use the SET_DEFAULT option, we must define the default parameter otherwise, Django will again recognize errors and not create migration files.

Book.author: (fields.E321) Field specifies on_delete=SET_DEFAULT, but has no default value.
HINT: Set a default value, or change the on_delete rule.

You can see SET_DEFAULT syntax in the next example.

class Author(models.Model):
    name = models.CharField(max_length=512)


class Book(models.Model):
    title = models.CharField(max_length=255)
    author = models.ForeignKey(Author, on_delete=models.SET_DEFAULT, default=2)

Our database looks as before.

Authors table.

idname
1William Shakespeare
2Miguel de Cervantes

Books table.

idtitleauthor_id
1Romeo and Juliet1
2Don Quixote2
>>>Author.objects.get(id=1).delete()

(1, {‘Author': 1})

The author is successfully deleted, let’s check our database tables now.

Authors table.

idname
2Miguel de Cervantes

Books table.

idtitleauthor_id
1Romeo and Juliet2
2Don Quixote2

As you can see, author_id changes and SET_DEFAULT works as expected.

NOTE - if your default value is not present in the other table, Django will raise an IntegrityError.
django.db.utils.IntegrityError: insert or update on table "book" violates foreign key constraint
DETAIL:  Key (author_id)=(1) is not present in table "author".

Django on_delete SET( )

SET option gives us the possibility to write functions where SET_DEFAULT has limitations. So what exactly are SET_DEFAULT limitations? First, SET_DEFAULT cant work with callable functions, and we can’t avoid executing queries at the time model is imported. Here SET choice comes in handy.

NOTE - SET is the only choice that receives a value. That value can be the return value of a function or a method.

Now something different.

class Author(models.Model):
    name = models.CharField(max_length=512)


def get_unknown_author():
    return Author.objects.get_or_create(name='Unknown Author')[0]


class Book(models.Model):
    title = models.CharField(max_length=255)
    author = models.ForeignKey(Author, on_delete=models.SET(get_unknown_author))

In models, we defined a function that is called when the Author of the book is deleted, we create ‘Unknown Author’ and pass the value to the Books connected to the previous author.

Let’s see this in practice.

Same as before, we start with the following tables.

Authors table.

idname
1William Shakespeare
2Miguel de Cervantes

Books table.

idtitleauthor_id
1Romeo and Juliet1
2Don Quixote2
>>>Author.objects.get(id=1).delete()

(1, {'Author': 1})

As expected, only the first author was deleted.

Now let’s check database tables.

Authors table.

idname
2Miguel de Cervantes
3Unknown Author

Books table.

idtitleauthor_id
1Romeo and Juliet3
2Don Quixote2

As you can see, on delete a new author was added to the Authors table and it was instantly assigned to the Book.

Finally, now we can check the DO_NOTHING choice.

Django on_delete DO_NOTHING

As the name suggests, this choice should do nothing, but what happens with foreign keys then? How does this choice work?

DO_NOTHING choice takes no action, and everything is in the hands of the database.

It’s best to dive into an example.

First, define our models.

class Author(models.Model):
    name = models.CharField(max_length=512)


class Book(models.Model):
    title = models.CharField(max_length=255)
    author = models.ForeignKey(Author, on_delete=models.DO_NOTHING)

And as always, we have this starting database structure. But we will add ‘Unknown Author’ not referenced with any books.

Authors table.

idname
1William Shakespeare
2Miguel de Cervantes
3Unknown Author

Books table.

idtitleauthor_id
1Romeo and Juliet1
2Don Quixote2
>>>Author.objects.get(id=3).delete()

(1, {'Author': 1})

Unreferenced Author is successfully deleted.

>>>Author.objects.get(id=1).delete()

django.db.utils.IntegrityError: update or delete on table "author" violates foreign key constraint
DETAIL:  Key (id)=(1) is still referenced from table "book".

Referenced Author cannot be deleted, and IntegrityError is raised.

After given commands, only ‘Unknown Author’ is deleted.

What on_delete choice to use?

CASCADEWhen you want to delete an instance containing a foreign key that is deleted.
PROTECTWhen you want to ensure that an instance cannot be deleted if it is referenced somewhere.
RESTRICTWhen you want to ensure that an instance cannot be deleted if it is referenced somewhere.
SET_NULLWhen you want set deleted foreign key to null.
SET_DEFAULTWhen you want to set the deleted foreign key to some predefined default value.
SET ( )When you want to set the deleted foreign key to some value using queries or callables.
DO_NOTHINGWhen you don’t want to do anything about the deletion.

Summary

Using knowledge gained by reading this article, you should now use on_delete with confidence.

Don’t be afraid to test some of the choices. 

A lot of developers use only CASCADE and PROTECT, but with some of these other options, you could save yourself a lot of manual implementation and time. 🙂