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

发布于 2024-11-19 23:02:23 字数 1325 浏览 2 评论 0原文

我有一个 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

雪化雨蝶 2024-11-26 23:02:23

在我的具体情况下,我将此错误追溯到 models.py 代码中的 ModelAdmin 内联声明,导致我的模型类无法正确实例化,而破坏的 on_delete 行为是其最明显的副作用。来自我的提交消息:

修复了 Django 中级联删除未正确完成的问题
数据库。

事实证明,不要在 models.py 的全局范围内声明 ModelAdmins 非常重要,否则所有关系
在所有模型之前计算不同模型之间的关系
加载,其中很多都被遗漏了。确实没那么强调
有关此内容,请参阅 Django 文档。

因此,在我原来的(损坏的)代码中,我会在每个模型之后立即声明 ModelAdmin,a 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 以便每个类的所有元类设置在所有模型声明都已处理后发生,然后所有关系再次开始正常运行。

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:

Fixed issue with cascading deletes not being done properly in Django
DB.

Turns out it's really important to not declare your ModelAdmins at the global scope in models.py, otherwise all the relationships
between the different models get calculated before all the models are
loaded and lots of them get left out. Really isn't that emphatic
about this in the Django documentation.

So in my original (broken) code, I would have the ModelAdmin for each Model declared immediately after it, a 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 )

The solution was to move all the ModelAdmin declarations out of myapp/models.py and into myapp/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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文