Prevent delete in Django model

dyve picture dyve · Jan 28, 2011 · Viewed 19.4k times · Source

I have a setup like this (simplified for this question):

class Employee(models.Model):
    name = models.CharField(name, unique=True)

class Project(models.Model):
    name = models.CharField(name, unique=True)
    employees = models.ManyToManyField(Employee)

When an Employee is about to get deleted, I want to check whether or not he is connected to any projects. If so, deletion should be impossible.

I know about signals and how to work them. I can connect to the pre_delete signal, and make it throw an exception like ValidationError. This prevents deletion but it is not handled gracefully by forms and such.

This seems like a situation that other will have run into. I'm hoping someone can point out a more elegant solution.

Answer

Elwin picture Elwin · Sep 25, 2013

I was looking for an answer to this problem, was not able to find a good one, which would work for both models.Model.delete() and QuerySet.delete(). I went along and, sort of, implementing Steve K's solution. I used this solution to make sure an object (Employee in this example) can't be deleted from the database, in either way, but is set to inactive.

It's a late answer.. just for the sake of other people looking I'm putting my solution here.

Here is the code:

class CustomQuerySet(QuerySet):
    def delete(self):
        self.update(active=False)


class ActiveManager(models.Manager):
    def active(self):
        return self.model.objects.filter(active=True)

    def get_queryset(self):
        return CustomQuerySet(self.model, using=self._db)


class Employee(models.Model):
    name = models.CharField(name, unique=True)
    active = models.BooleanField(default=True, editable=False)

    objects = ActiveManager()

    def delete(self):
        self.active = False
        self.save()

Usage:

Employee.objects.active() # use it just like you would .all()

or in the admin:

class Employee(admin.ModelAdmin):

    def queryset(self, request):
        return super(Employee, self).queryset(request).filter(active=True)