I have a Django 1.3 app for which I am using South 0.7.3 for DB migrations. I have a problem where an on_delete=models.SET_NULL rule doesn’t seem to be firing when the parent entity is deleted, thus giving me a constraint violation from the underlying DB (which is Postgres 8.4).
The relevant parts of the entity defns are:
class AccessPeriod:
....
class Payment:
period = models.ForeignKey(
AccessPeriod, related_name = "payments", db_index = True,
null = True, on_delete = models.SET_NULL )
After some digging, I discovered that South doesn’t actually insert the ON DELETE clause into the SQL it generates for migrations, so the DB is definitely not going to do the nullifying on the broken relationships itself:
http://south.aeracode.org/ticket/763
Then I read the Django docs for on_delete rules, which state (my emphasis):
When an object referenced by a ForeignKey is deleted, Django by
default emulates the behavior of the SQL constraint ON DELETE CASCADE
and also deletes the object containing the ForeignKey. This behavior
can be overridden by specifying the on_delete argument.
The “emulates” part suggested to me that Django tries to implement the on_delete behaviour itself and does not rely on the underlying DB to automatically execute this as part of the transaction, thus making the South bug irrelevant.
I had a poke through db/models/deletion.py in the Django source and based on the implementation of SET() / SET_NULL() and collect() it certainly seems like Django is supposed to do this itself, however, if I try to delete an AccessPeriod from the Django admin, I get constraint violations on the Payments table for the ID it still references which is now deleted, i.e. it doesn’t seem like Django is calling SET_NULL() on the Payment.period relationship as part of the call to accessPeriod.delete().
Am I doing something wrong here, or misunderstanding what Django should be doing? Just trying to avoid manually hacking the DB to insert the ON DELETE rule myself, which feels extremely brittle and horrible.
In my specific case, I traced this bug down to declarations of ModelAdmin inline in my models.py code causing my model classes to be instantiated incorrectly, with broken
on_deletebehaviour the most visible side-effect of this. From my commit message:So in my original (broken) code, I would have the ModelAdmin for each Model declared immediately after it, a la:
The solution was to move all the ModelAdmin declarations out of
myapp/models.pyand intomyapp/admin.pyso that all the metaclass setup for each class happens after all the model declarations have been handled, and then all the relationships started behaving properly again.