为什么我的 GenericForeignKey 删除时不会级联?

发布于 2024-11-25 13:09:45 字数 578 浏览 0 评论 0原文

我正在创建一个自定义评论系统,它可以使用内容类型 GenericForeignKey 将评论附加到任何模型。

class Comment(models.Model):
    body = models.TextField(verbose_name='Comment')
    user = models.ForeignKey(User)
    parent = models.ForeignKey('self', null=True, blank=True)
    created = models.DateTimeField(auto_now_add=True)
    content_type = models.ForeignKey(ContentType)
    object_id = models.PositiveIntegerField()
    content_object = generic.GenericForeignKey('content_type', 'object_id')

据我了解,当删除附加注释的模型时,删除操作应该级联并删除注释。

不幸的是,这并没有发生,我被难住了。默认删除行为发生变化是否有任何常见原因?

I'm creating a custom commenting system which can attache comments to any model using the contenttypes GenericForeignKey.

class Comment(models.Model):
    body = models.TextField(verbose_name='Comment')
    user = models.ForeignKey(User)
    parent = models.ForeignKey('self', null=True, blank=True)
    created = models.DateTimeField(auto_now_add=True)
    content_type = models.ForeignKey(ContentType)
    object_id = models.PositiveIntegerField()
    content_object = generic.GenericForeignKey('content_type', 'object_id')

It is my understanding that when the model the comment is attached to is deleted, the delete should cascade and remove the comment as well.

Unfortunately, this isn't happening and I'm stumped. Are there any common reasons why the default delete behaviour would change?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

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

发布评论

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

评论(3

小…红帽 2024-12-02 13:09:45

不,文档没有这么说。它的意思是,如果您在模型上定义 GenericRelation - 即 GenericForeignKey 的反面 - 那么当删除具有通用 FK 的项目时,具有通用 FK 的项目GenericRelation 也将被删除。

与ForeignKey不同,GenericForeignKey不接受on_delete
自定义此行为的参数;如果需要,您可以避免
只需不使用 GenericRelation 即可进行级联删除,并替换
可以通过 pre_delete 信号提供行为。

No, the documentation doesn't say that. What it says is that if you define a GenericRelation on a model - ie the reverse side of the GenericForeignKey - then when the item with the generic FK is deleted, the item with the GenericRelation will also be deleted.

Unlike ForeignKey, GenericForeignKey does not accept an on_delete
argument to customize this behavior; if desired, you can avoid the
cascade-deletion simply by not using GenericRelation, and alternate
behavior can be provided via the pre_delete signal.

忱杏 2024-12-02 13:09:45

我意识到这是一个非常的问题,所以事情可能与提出这个问题时有所不同,但接受的答案让我今天早上追寻了一个兔子洞,因此我想将其留在这里以防止子孙后代分担我的痛苦。

来自文档:

另请注意,如果删除具有 GenericRelation 的对象,任何具有 GenericForeignKey 指向它的对象也将被删除。在上面的示例中,这意味着如果删除 Bookmark 对象,则指向它的任何 TaggedItem 对象也会同时删除。

这与公认的答案相反。想象一下:

class Comment(models.Model):
    body = models.TextField(verbose_name='Comment')
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
    object_id = models.PositiveIntegerField()
    content_object = generic.GenericForeignKey('content_type', 'object_id')

class Post(models.Model):
    comment = GenericRelation(Comment)

在上面的示例中,如果您的 Comment 对象有一个指向 Post 对象的通用外键,那么当删除 Post 对象时,指向它的任何 Comment 对象也将被删除。

这是预期的行为,其操作方式与普通外键相同。还是用上面的例子,如果Comment对象指向的User对象被删除,那么Comment也会被删除。

如果您碰巧遇到这个问题,因为您需要这种行为的相反,即当您删除评论时,帖子也会被删除,那么您可能需要使用 信号

I realise this is a very old question so it's possible things are different from when this was asked, but the accepted answer had me chasing down a rabbit hole this morning, therefore I wanted to leave this here to prevent future generations sharing my pain.

From the docs:

Note also, that if you delete an object that has a GenericRelation, any objects which have a GenericForeignKey pointing at it will be deleted as well. In the example above, this means that if a Bookmark object were deleted, any TaggedItem objects pointing at it would be deleted at the same time.

This is the opposite of what the accepted answer is saying. Imagine the following:

class Comment(models.Model):
    body = models.TextField(verbose_name='Comment')
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
    object_id = models.PositiveIntegerField()
    content_object = generic.GenericForeignKey('content_type', 'object_id')

class Post(models.Model):
    comment = GenericRelation(Comment)

In the above example, if your Comment object has a Generic Foreign Key pointing to a Post object, then when the Post object is deleted any Comment objects pointing to it will also be deleted.

This is the expected behaviour and operates the same as a normal ForeignKey. Using the same example above, if the User object that the Comment object points to is deleted, the Comment will also be deleted.

If you happen to chance across this question because you need the reverse of this behaviour, i.e. when you delete the Comment the Post is also deleted, then you will likely need to employ the power of signals.

丶情人眼里出诗心の 2024-12-02 13:09:45

除了之前的答案之外 - 如果您有更复杂的结构和类似 GenericOneToOne 的东西(在 Django 中不直接存在):

class Post(models.Model)
    title = models.CharField(max_length=100)

class Comment(models.Model):
    post = models.ForeignKey(Post)
    body = models.TextField(verbose_name='Comment')
    content_type = models.ForeignKey(ContentType)
    object_id = models.PositiveIntegerField()
    content_object = generic.GenericForeignKey('content_type', 'object_id')

    class Meta:
        # Constrain equals to OneToOne relation.
        unique_together = ('content_type', 'object_id')

class Author(models.Model):
    comment = GenericRelation(Comment)
    name = models.CharField(max_length=100)

并且您想要删除 Post 并成为确保 CommentAuthor 也被删除,您需要编写自定义 post_delete 信号:

from django.db.models.signals import post_delete
from django.dispatch import receiver

@receiver(post_delete, sender=Comment, dispatch_uid='delete_comment_content_object')
def delete_comment_content_object(sender, instance, using, **kwargs):
    instance.content_object.delete()

如果您重写 delete 方法Comment 类像这样:

def delete(self, *args, **kwargs):
    self.content_object.delete()
    super().delete(args, kwargs)

如果您删除评论,它会删除作者。如果删除 Post Author 对象将保留在数据库中。

In addition to previous answers - if you have more complex structure and something like GenericOneToOne (which is not present in straight way in Django):

class Post(models.Model)
    title = models.CharField(max_length=100)

class Comment(models.Model):
    post = models.ForeignKey(Post)
    body = models.TextField(verbose_name='Comment')
    content_type = models.ForeignKey(ContentType)
    object_id = models.PositiveIntegerField()
    content_object = generic.GenericForeignKey('content_type', 'object_id')

    class Meta:
        # Constrain equals to OneToOne relation.
        unique_together = ('content_type', 'object_id')

class Author(models.Model):
    comment = GenericRelation(Comment)
    name = models.CharField(max_length=100)

And you want to delete Post and be sure that Comment and Author are deleted as well you need to write custom post_delete signal:

from django.db.models.signals import post_delete
from django.dispatch import receiver

@receiver(post_delete, sender=Comment, dispatch_uid='delete_comment_content_object')
def delete_comment_content_object(sender, instance, using, **kwargs):
    instance.content_object.delete()

If you override delete method of Comment class like this:

def delete(self, *args, **kwargs):
    self.content_object.delete()
    super().delete(args, kwargs)

It will delete Author only if you delete Comment. If you delete Post Author object will stay in database.

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