Well, now I'm using Django 1.6+
And I have a model:
class FileReference(models.Model):
# some data fields
# ...
pass
class Person(models.Model):
avatar = models.ForeignKey(FileReference, related_name='people_with_avatar')
class House(models.Model):
images = models.ManyToManyField(FileReference, related_name='houses_with_images')
class Document(model.Model):
attachment = models.OneToOneField(FileReference, related_name='document_with_attachment')
So, many other model will have a foreign key referring to the FileReference
model.
But sometimes, the referring models is deleted, with the FileReference
object left.
I want to delete the FileReference
objects with no foreign key referencing.
But so many other places will have foreign keys.
Is there any efficient way to find all the references? i.e. get the reference count of some model object?
I stumbled upon this question and I got a solution for you. Note, that django==1.6
is not supported any more, so this solution will probably work on django>=1.9
Lets say we are talking about 2 of the objects for now:
class FileReference(models.Model):
pass
class Person(models.Model):
avatar = models.ForeignKey(FileReference, related_name='people_with_avatar', on_delete=models.CASCADE)
As you can see in ForeignKey.on_delete documentation, when you delete the related FileReference
object, the referenced object Person
is deleted as well.
Now for your question. How do we do the revered? We want upon Person
deletion that FileReference
object will be removed as well.
We will do that using post_delete signal:
def delete_reverse(sender, **kwargs):
try:
if kwargs['instance'].avatar:
kwargs['instance'].avatar.delete()
except:
pass
post_delete.connect(delete_reverse, sender=Person)
What we did there was deleting the reference in avatar
field on Person
deletion. Notice that the try: except:
block is to prevent looping exceptions.
Extra:
The above solution will work on all future objects. If you want to remove all of the past objects without a reference do the following:
In your package add the following file and directories: management/commands/remove_unused_file_reference.py
from django.core.management.base import BaseCommand, CommandError
class Command(BaseCommand):
def handle(self, *args, **options):
file_references = FileReference.objects.all()
file_reference_mapping = {file_reference.id: file_reference for file_reference in file_references}
persons = Person.objects.all()
person_avatar_mapping = {person.avatar.id: person for person in persons}
for file_reference_id, file_reference in file_reference_mapping.items():
if file_reference_id not in person_avatar_mapping:
file_reference.delete()
When you done, call: python manage.py remove_unused_file_reference
This is the base idea, you can change it to bulk delete etc...
I hope this will help to someone out there. Good Luck!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With