Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django delete FileField

I'm building a web app in Django. I have a model that uploads a file, but I can not delete the file. Here is my code:

class Song(models.Model):
    name = models.CharField(blank=True, max_length=100)
    author = models.ForeignKey(User, to_field='id', related_name="id_user2")
    song = models.FileField(upload_to='/songs/')
    image = models.ImageField(upload_to='/pictures/', blank=True)
    date_upload = models.DateField(auto_now_add=True)

    def delete(self, *args, **kwargs):
        # You have to prepare what you need before delete the model
        storage, path = self.song.storage, self.song.path
        # Delete the model before the file
        super(Song, self).delete(*args, **kwargs)
        # Delete the file after the model
        storage.delete(path)

Then, in python manage.py shell I do this:

song = Song.objects.get(pk=1)
song.delete()

It deletes the record from the database but not the file on server. What else can I try?

Thanks!

like image 818
Marcos Aguayo Avatar asked Oct 16 '22 15:10

Marcos Aguayo


People also ask

What is Django cleanup?

The django-cleanup app automatically deletes files for FileField , ImageField and subclasses. When a FileField 's value is changed and the model is saved, the old file is deleted. When a model that has a FileField is deleted, the file is also deleted.

How do you delete a file in Python?

remove() method in Python can be used to remove files, and the os. rmdir() method can be used to delete an empty folder. The shutil. rmtree() method can be used to delete a folder along with all of its files.


1 Answers

Before Django 1.3, the file was deleted from the filesystem automatically when you deleted the corresponding model instance. You are probably using a newer Django version, so you'll have to implement deleting the file from the filesystem yourself.

Simple signal-based sample

My method of choice at the time of writing is a mix of post_delete and pre_save signals, which makes it so that obsolete files are deleted whenever corresponding models are deleted or have their files changed.

Based on a hypothetical MediaFile model:

import os
import uuid

from django.db import models
from django.dispatch import receiver
from django.utils.translation import ugettext_lazy as _


class MediaFile(models.Model):
    file = models.FileField(_("file"),
        upload_to=lambda instance, filename: str(uuid.uuid4()))


# These two auto-delete files from filesystem when they are unneeded:

@receiver(models.signals.post_delete, sender=MediaFile)
def auto_delete_file_on_delete(sender, instance, **kwargs):
    """
    Deletes file from filesystem
    when corresponding `MediaFile` object is deleted.
    """
    if instance.file:
        if os.path.isfile(instance.file.path):
            os.remove(instance.file.path)

@receiver(models.signals.pre_save, sender=MediaFile)
def auto_delete_file_on_change(sender, instance, **kwargs):
    """
    Deletes old file from filesystem
    when corresponding `MediaFile` object is updated
    with new file.
    """
    if not instance.pk:
        return False

    try:
        old_file = MediaFile.objects.get(pk=instance.pk).file
    except MediaFile.DoesNotExist:
        return False

    new_file = instance.file
    if not old_file == new_file:
        if os.path.isfile(old_file.path):
            os.remove(old_file.path)
  • I think one of the apps I’ve built a while back used this code in production, but nevertheless use at your own risk.
  • For example, there’s a possible data loss scenario: your data might end up referencing a nonexistent file if your save() method call happens to be within a transaction that gets rolled back. You could consider wrapping file-removing logic into transaction.on_commit(), along the lines of transaction.on_commit(lambda: os.remove(old_file.path)), as suggested in Mikhail’s comment. django-cleanup library does something along those lines.
  • Edge case: if your app uploads a new file and points model instance to the new file without calling save() (e.g. by bulk updating a QuerySet), the old file will keep lying around because signals won’t be run. This doesn’t happen if you use conventional file handling methods.
  • Coding style: this example uses file as field name, which is not a good style because it clashes with the built-in file object identifier.

Addendum: periodic cleanup

Realistically, you may want to also run a periodic task to handle orphan file cleanup in case a runtime failure prevents some file from being removed. With that in mind, you could probably get rid of signal handlers altogether, and make such a task the mechanism for dealing with insensitive data and not-so-large files.

Either way though, if you are handling sensitive data, it’s always better to double- or triple- check that you never fail to timely delete data in production to avoid any associated liabilities.

See also

  • FieldFile.delete() in Django 1.11 model field reference (note that it describes the FieldFile class, but you’d call .delete() directly on the field: FileField instance proxies to the corresponding FieldFile instance, and you access its methods as if they were field’s)

    Note that when a model is deleted, related files are not deleted. If you need to cleanup orphaned files, you’ll need to handle it yourself (for instance, with a custom management command that can be run manually or scheduled to run periodically via e.g. cron).

  • Why Django doesn’t delete files automatically: entry in release notes for Django 1.3

    In earlier Django versions, when a model instance containing a FileField was deleted, FileField took it upon itself to also delete the file from the backend storage. This opened the door to several data-loss scenarios, including rolled-back transactions and fields on different models referencing the same file. In Django 1.3, when a model is deleted the FileField’s delete() method won’t be called. If you need cleanup of orphaned files, you’ll need to handle it yourself (for instance, with a custom management command that can be run manually or scheduled to run periodically via e.g. cron).

  • Example of using a pre_delete signal only

like image 164
Anton Strogonoff Avatar answered Nov 15 '22 23:11

Anton Strogonoff