What are the options for overriding Django's cascading delete behaviour?

Tom picture Tom · Mar 19, 2010 · Viewed 46.7k times · Source

Django models generally handle the ON DELETE CASCADE behaviour quite adequately (in a way that works on databases that don't support it natively.)

However, I'm struggling to discover what is the best way to override this behaviour where it is not appropriate, in the following scenarios for example:

  • ON DELETE RESTRICT (i.e. prevent deleting an object if it has child records)

  • ON DELETE SET NULL (i.e. don't delete a child record, but set it's parent key to NULL instead to break the relationship)

  • Update other related data when a record is deleted (e.g. deleting an uploaded image file)

The following are the potential ways to achieve these that I am aware of:

  • Override the model's delete() method. While this sort of works, it is sidestepped when the records are deleted via a QuerySet. Also, every model's delete() must be overridden to make sure Django's code is never called and super() can't be called as it may use a QuerySet to delete child objects.

  • Use signals. This seems to be ideal as they are called when directly deleting the model or deleting via a QuerySet. However, there is no possibility to prevent a child object from being deleted so it is not usable to implement ON CASCADE RESTRICT or SET NULL.

  • Use a database engine that handles this properly (what does Django do in this case?)

  • Wait until Django supports it (and live with bugs until then...)

It seems like the first option is the only viable one, but it's ugly, throws the baby out with the bath water, and risks missing something when a new model/relation is added.

Am I missing something? Any recommendations?

Answer

blasio picture blasio · Dec 8, 2010

Just a note for those who run into this issue as well, there is now an built-in solution in Django 1.3.

See the details in the documentation django.db.models.ForeignKey.on_delete Thanks for editor of Fragments of Code site to point it out.

The simplest possible scenario just add in your model FK field definition:

on_delete=models.SET_NULL