Created
September 27, 2016 14:43
-
-
Save freewayz/69d1b8bcb3c225bea57bd70ee1e765f8 to your computer and use it in GitHub Desktop.
Django check if model has related object before Deleting the model
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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() |
related = rel.related_model.objects.filter(**{rel.field.name: self})
I suggest to change with ->
related = rel.related_model._base_manager.filter(**{rel.field.name: self})
https://docs.djangoproject.com/en/3.2/topics/db/managers/#base-managers
As docs says it prefer to use _base_manager
when default manager (objects) retrieve a subset of records, for example only instance with is_deleted=True .
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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
And in the View, something like