Skip to content

Instantly share code, notes, and snippets.

@freewayz
Created September 27, 2016 14:43
Show Gist options
  • Save freewayz/69d1b8bcb3c225bea57bd70ee1e765f8 to your computer and use it in GitHub Desktop.
Save freewayz/69d1b8bcb3c225bea57bd70ee1e765f8 to your computer and use it in GitHub Desktop.
Django check if model has related object before Deleting the model
#After looking for a way to check if a model instance can be deleted in django ,
#i came across many sample, but was not 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()
@openHBP
Copy link

openHBP commented May 8, 2020

Marvelous, that was exactly what I need! Until django models.RESTRICT appears in production!
Small improvement though, in order to return ALL related field directly to the user

    def is_deletable(self):
        related_list = []
        # 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():
                    related_list.append(related)
                    # if there is return a Tuple of flag = False the related_model object
            except AttributeError:  # an attribute error for field occurs when checking for AutoField
                pass  # just pass as we dont need to check for AutoField
        return related_list

And in the View, something like

    related_list = a_org.is_deletable()
    
    if related_list:
    	# do some stuff with the related_obj list
        messages.error(request,
		_("To delete the organisation {0}, "delete all referring records first!").format(a_org)
            )
	    for related in related_list:
		related_model = related.model._meta.verbose_name
		related_values = [i.__str__() for i in related.all()]
		msg = "{0}: {1}".format(related_model, related_values)
		messages.warning(request, msg)
	    return redirect('my_detail_url')    

    else:
    	# call the delete function
    	a_org.delete()

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment