Django应该自己实现DB on_delete规则吗?

时间:2011-07-11 13:04:16

标签: django orm constraints django-south

我有一个Django 1.3应用程序,我使用South 0.7.3进行数据库迁移。我有一个问题,当父实体被删除时,on_delete=models.SET_NULL规则似乎没有触发,因此我从基础数据库(Postgres 8.4)中违反约束。

实体定义的相关部分是:

class AccessPeriod:
    ....

class Payment:
    period = models.ForeignKey( 
        AccessPeriod, related_name = "payments", db_index = True,
        null = True, on_delete = models.SET_NULL )

经过一番挖掘后,我发现South实际上并没有将ON DELETE子句插入到它为迁移生成的SQL中,因此DB绝对不会对破坏的关系本身进行无效处理:

http://south.aeracode.org/ticket/763

然后我读了关于on_delete规则的Django文档,其中说明了(我的重点):

  

删除ForeignKey引用的对象时,Django by   默认模拟 SQL约束ON DELETE CASCADE的行为   并删除包含ForeignKey的对象。这种行为   可以通过指定on_delete参数来覆盖。

“模拟”部分向我建议Django尝试实现on_delete行为本身并且不依赖于底层数据库作为事务的一部分自动执行它,从而使South bug无关紧要。

我在Django源代码中点击了db/models/deletion.py并基于SET() / SET_NULL()collect()的实现,看起来似乎Django应该这样做但是,如果我尝试从Django管理员中删除一个AccessPeriod,我会在Payments表上获取它仍然引用的ID的约束违规,现在它已被删除,即看起来Django似乎没有调用SET_NULL()作为Payment.period调用的一部分,accessPeriod.delete()关系。

我在这里做错了什么,或者误解了Django应该做什么?只是试图避免手动黑客攻击数据库自己插入ON DELETE规则,感觉非常脆弱和可怕。

1 个答案:

答案 0 :(得分:4)

在我的特定情况下,我将此错误追溯到我的models.py代码中的ModelAdmin内联声明,导致我的模型类被错误地实例化,其中on_delete行为损坏是最明显的副作用。从我的提交消息:

  

修复了在Django中未正确完成级联删除的问题   DB。

     

事实证明,在models.py中不在全局范围内声明ModelAdmins非常重要,否则所有关系都是如此   在所有模型之前计算不同模型之间的差异   加载了很多,他们被排除在外。真的不是那么强调   关于Django文档中的这个。

所以在我原来的(破碎的)代码中,我会在它之后立即声明每个Model的ModelAdmin,la:

class AccessPeriod( models.Model ):
    ....

class AccessPeriodAdmin( models.ModelAdmin ):
    ....

# This causes the metaclass setup for AccessPeriod to happen right now,
# and since related models for AccessPeriod are not all declared yet,
# relationship handling behaviour becomes broken for AccessPeriod
admin.site.register( AccessPeriod, AccessPeriodAdmin )

class Payment( models.Model ):
    ....

class PaymentAdmin( models.ModelAdmin ):
    ....

# Same effect on the Payment model here    
admin.site.register( Payment, PaymentAdmin )

解决方案是将所有ModelAdmin声明从myapp/models.py移出myapp/admin.py,以便在处理完所有模型声明之后,每个类的所有元类设置都会发生,然后是所有关系开始表现得很好了。