Search code examples
djangodjango-signalsdjango-uploads

How to delete files from filesystem using post_delete - Django 1.8


I have a model - Product, which contains a thumbnail image. I have another model which contains images associated with the product - ProductImage. I want to delete both the thumbnail and the images from the server when the product instance is deleted, and for a while this seemed to worked, but not anymore.

Relevant code...

Class Product(models.Model):
    title = Charfield
    thumbnail = ImageField(upload_to='thumbnails/', verbose_name='thumbnail', blank=True, )

Class ProductImage(models.Model):
    product = models.ForeignKey(plant, default=None, related_name='images')
    image = models.ImageField(upload_to='images/', verbose_name='image',)

The following delete method (in the product class) was working, but I changed my code and it no longer works - and from what i have read it is best practice to use post_delete, rather then override delete()

def delete(self):
    images = ProductImage.objects.filter(product=self)
    if images:
        for image in images:
            image.delete()
    super(Product, self).delete()

How can I rewrite a delete method which will achieve what I want? I have tried to use post_delete but so far I have been unsuccessful because I am not sure how to apply it when it comes to deleting the ProductImage instance...


Solution

  • And here's an example with the post_delete:

    import os
    from django.db import models
    
    def _delete_file(path):
       """ Deletes file from filesystem. """
       if os.path.isfile(path):
           os.remove(path)
    
    @receiver(models.signals.post_delete, sender=ProductImage)
    def delete_file(sender, instance, *args, **kwargs):
        """ Deletes image files on `post_delete` """
        if instance.image:
            _delete_file(instance.image.path)
    
    @receiver(models.signals.post_delete, sender=Product)
    def delete_file(sender, instance, *args, **kwargs):
        """ Deletes thumbnail files on `post_delete` """
        if instance.thumbnail:
            _delete_file(instance.thumbnail.path)
    

    Overriding the delete() method:

    class Product(models.Model):
        ...
    
        def delete(self):
            images = ProductImage.objects.filter(product=self)
            for image in images:
                image.delete()
            self.thumbnail.delete()
            super(Product, self).delete()
    
    
    class ProductImage(models.Model):
        ...
    
        def delete(self):
            self.image.delete()
            super(ProductImage, self).delete()
    

    Read about Cascade delete: docs