2

After looking for a way to check if a model instance can be deleted in django, I found many options, but none was working as expected. Hope this solution can help.

Let start by creating an Abstract model class which can be inherited by other model

class ModelIsDeletable(models.Model):
    name = models.CharField(max_length=200, blank=True, null=True, unique=True)
    description = models.CharField(max_length=200, blank=True, null=True)
    date_modified = models.DateTimeField(auto_now_add=True)

    def is_deletable(self):
        # get all the related object
        for rel in self._meta.get_fields():
            try:
                # check if there is a relationship with at least one related object
                related = rel.related_model.objects.filter(**{rel.field.name: self})
                if related.exists():
                    # if there is return a Tuple of flag = False the related_model object
                    return False, related
            except AttributeError:  # an attribute error for field occurs when checking for AutoField
                pass  # just pass as we dont need to check for AutoField
        return True, None

     class Meta:
        abstract = True

Example

So let say we have three model Organization and Department and StaffType So many Department can be in an Organization And an Organization has a particular StaffType

class StaffType(ModelIsDeletable):
    pensionable = models.BooleanField(default=False)

class Organization(ModelIsDeletable):
    staff_type = models.ForeignKey(to=StaffType)


class Department(ModelIsDeletable):
    organization = models.ForeignKey(to=Organization, to_field="id")

so let say after adding some information you want to remove an organization model instance that is already tied to a Department

For instance we have Organization Table => (name = Engineering, pk = 1) Department Table => (name=Developer, organization_fk=1, pk=1)

Now when you try to delete an organization after get it with the pk

a_org = Organization.objects.get(pk=1)

With this at hand you can check if it deletable

deletable, related_obj = a_org.is_deletable()

if not deletable:
    # do some stuff with the related_obj list

else:
    # call the delete function
    a_org.delete()
gawi
  • 2,843
  • 4
  • 29
  • 44
pitaside
  • 650
  • 10
  • 14

1 Answers1

3

Your question appears to be "How to detect what related model objects would be deleted if I delete this model object?" or "How to detect what related rows would be deleted if I delete this row?"

Another option is to use a transaction, do the delete, save the information provided by django, but rollback before committing the change. This works in databases like Postgres and MySQL, but I don't know about others.

In this example I want to know what will be deleted if I delete my organization named 'pdemo', and I see that it has 408 related Property objects.

https://gist.github.com/cgthayer/25aa97bb4b74efb75e3467fb7bbdaacb

>>> from django.db import transaction
>>> transaction.set_autocommit(autocommit=False)
>>> o = Organization_v2.objects.get(name='pdemo')
>>> del_info = o.delete()
>>> del_info
(1404, {'data.Property': 408, [..more stuff..], 'data.Organization_v2': 1})
>>> Property.objects.filter(scope__organization_id=o).count()
0
>>> transaction.rollback()
>>> o = Organization_v2.objects.get(name='pdemo')
>>> Property.objects.filter(scope__organization_id=o).count()
408

This could be translated into a generic function.

When looking into this, I found many old solutions that use the functions in the django.contrib.admin to determine this, but that's an undocumented api that seems to change from time to time, so using transactions seems to be easier iff your database supports it.

Charles Thayer
  • 1,678
  • 1
  • 13
  • 17