Django 应该自己实现 DB on_delete 规则吗?
我有一个 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 中,因此数据库肯定不会这样做破坏关系本身的无效:
http://south.aeracode.org/ticket/763
然后我读了 Django文档用于on_delete 规则,其中规定(我的重点):
当一个ForeignKey引用的对象被删除时,Django通过 默认模拟 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 似乎没有在调用 accessPeriod.delete()
的过程中对 Payment.period
关系调用 SET_NULL()
。
我在这里做错了什么,或者误解了 Django 应该做什么?只是试图避免自己手动破解数据库来插入 ON DELETE 规则,这感觉非常脆弱和可怕。
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.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
在我的具体情况下,我将此错误追溯到 models.py 代码中的 ModelAdmin 内联声明,导致我的模型类无法正确实例化,而破坏的
on_delete
行为是其最明显的副作用。来自我的提交消息:因此,在我原来的(损坏的)代码中,我会在每个模型之后立即声明 ModelAdmin,a la:
解决方案是将所有 ModelAdmin 声明从
myapp/models.py
移出并移入myapp/admin.py
以便每个类的所有元类设置在所有模型声明都已处理后发生,然后所有关系再次开始正常运行。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_delete
behaviour 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.py
and intomyapp/admin.py
so 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.