Michael
Michael

Reputation: 4461

GenericForeignKey and on_delete=models.PROTECT

Django 1.10

Say, I have an instance of Frame and two comments for it. Key moment: on_delete=models.PROTECT in the Comment model.

In the shell:

Comment.objects.all() 
<QuerySet [<Comment: Some comment.>, <Comment:
Second comment.>]

Then I delete frame instance (call FrameDelete). And:

Comment.objects.all()
<QuerySet []>

Empty. Deleted all comments. And models.PROTECT didn't help.

Well, I can't make it catch IntegrityError. Could you tell me if it is possible and how to do?

class FrameDelete(IntegrityErrorMixin, DeleteView):
    model = Frame

class IntegrityErrorMixin():
    def delete(self, request, *args, **kwargs):
        self.object = self.get_object()
        success_url = self.get_success_url()
        try:
            self.object.delete()
        except IntegrityError as err:
            raise PermissionDenied

        return HttpResponseRedirect(success_url)

class Frame(models.Model):
    .....
    comments = GenericRelation(Comment)

class Comment(models.Model):
    date = models.DateTimeField(null=False,
                            blank=False,
                            auto_now_add=True)

    author = models.ForeignKey(User, on_delete=models.PROTECT)
    body = models.TextField(blank=False,
                            null=False,
                            default="",
                            verbose_name = "",) # Empty. No need to show the verbose_name on the form.

    content_type = models.ForeignKey(ContentType, on_delete=models.PROTECT)
    object_id = models.PositiveIntegerField()
    content_object = GenericForeignKey('content_type', 'object_id')

Upvotes: 1

Views: 3184

Answers (1)

knbk
knbk

Reputation: 53679

You are passing on_delete=models.PROTECT to the foreign key to ContentType. This will only have an effect when you delete the content type, not when you delete the comment.

The documentation states:

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.

So to simulate the behavior of models.PROTECT, you need to attach a pre_delete signal that raises an exception if any related comments exist, something like this:

from django.db.models import ProtectedError, signals

@receiver(signals.pre_delete, Frame)
def protect_delete(sender, instance, **kwargs):
    if instance.comments.exists():
        raise ProtectedError()

Upvotes: 10

Related Questions