Почему мой GenericForeignKey не удаляется при удалении? - PullRequest
18 голосов
/ 24 июля 2011

Я создаю собственную систему комментирования, которая может прикреплять комментарии к любой модели с использованием типов содержимого 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')

Насколько я понимаю, когда модель, к которой прикреплен комментарий, удаляется, удалениеследует каскадировать и удалить комментарий.

К сожалению, этого не происходит, и я в тупике.Существуют ли общие причины, по которым поведение удаления по умолчанию изменится?

Ответы [ 3 ]

27 голосов
/ 24 июля 2011

Нет, в документации этого не сказано.Это говорит о том, что если вы определяете GenericRelation для модели - т.е. обратную сторону GenericForeignKey - тогда при удалении элемента с общим FK элемент с GenericRelation также будет удален.

В отличие от ForeignKey, GenericForeignKey не принимает аргумент on_delete для настройки этого поведения;при желании вы можете избежать каскадного удаления, просто не используя GenericRelation, и альтернативное поведение может быть обеспечено с помощью сигнала pre_delete.

5 голосов
/ 04 мая 2018

Я понимаю, что это очень старый вопрос, поэтому возможно, что вещи отличаются от того, когда его спрашивали, но принятый ответ заставил меня преследовать кроличью нору сегодня утром, поэтому я хотел оставить это здесь чтобы будущие поколения не разделяли мою боль.

Из документов:

Также обратите внимание, что если вы удалите объект, имеющий 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, указывающие на него, также будут удалены.

Это ожидаемое поведение и работает так же, как обычный ForeignKey. Используя тот же пример, приведенный выше, если объект User, на который указывает объект Comment, удален, комментарий также будет удален.

Если вы случайно натолкнулись на этот вопрос, потому что вам нужно обратное поведение, т. Е. Когда вы удаляете комментарий, пост также удаляется, тогда вам, вероятно, придется использовать силу сигналов .

0 голосов
/ 10 августа 2018

В дополнение к предыдущим ответам - если у вас более сложная структура и что-то вроде 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и убедитесь, что Comment и Author также удалены, вам нужно написать собственный 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)

Будет удалено Author только , если вы удалите Comment.При удалении Post Author объект останется в базе данных.

...