Deleting uploaded files in Django
Asked Answered
M

5

10

I have the following code to delete a file:

from django.db import models
from django import forms
import os

class Document(models.Model):
    docfile = models.FileField(upload_to='documents/%Y/%m/%d')
    def __unicode__(self):
        return '%s' % (self.docfile.name)
    def delete(self, *args, **kwargs):
        os.rmdir(os.path.join(settings.MEDIA_ROOT, self.docfile.name))
        super(Document,self).delete(*args,**kwargs)

It manages to delete the objects I ask it to in my views.py but when I reupload a file of the same name it seems as though the original file still exists since I'll get "output_1.txt" instead of "output.txt".

This is the code I use to delete:

def delete_matrix():
    documents = Document.objects.all()
    documents.delete()

Am I not deleting the file from the database? Any help would be appreciated.

Moorland answered 15/7, 2013 at 20:57 Comment(0)
F
14

Your problem is that you are overriding the delete() method on the model but you are calling the delete method on the QuerySet returned by the default manager (Documents.object.all().delete()). These are 2 separate methods so there are 2 ways of fixing this.

1.In the delete method of the model, replace the line

os.rmdir(os.path.join(settings.MEDIA_ROOT, self.docfile.name))

by

os.remove(os.path.join(settings.MEDIA_ROOT, self.docfile.name))

AND, call the delete method for each object separately. Replace

Document.objects.all().delete()

with

documents = Document.objects.all()
for document in documents:
    document.delete()

2.Replace the default manager to return a custom QuerySet which overrides the delete() method. This is explained in Overriding QuerySet.delete() in Django

Fanfaronade answered 15/7, 2013 at 21:34 Comment(2)
no I didn't get an exception, os.remove didn't seem to fix the error either.Moorland
We ended up with the same answer! I fixed this last night and I realized I shouldn't be overriding the delete method at all, I just added os.remove(...) after document.delete()Moorland
N
1

Try this

document = Document.objects.get(pk=pk)
# if `save`=True, changes are saved to the db else only the file is deleted
document.docfile.delete(save=True)
Neodymium answered 28/10, 2019 at 22:11 Comment(0)
T
1

You can use a much simpler code:

def delete(self, *args, **kwargs):
    if self.docfile:
        self.docfile.delete()
    super().delete(*args, **kwargs)
Troup answered 1/10, 2021 at 14:22 Comment(0)
S
1

I would subscribe to the model's post_delete signal instead, to ensure that the clean-up code is called regardless of whether the deletion was initiated by calling the model method or the QuerySet method.

Put this in your appname/signal_callbacks.py file:

from django.db.models.signals import post_delete
from django.dispatch import receiver

from .models import Document

@receiver(
    post_delete,
    sender=Document,
    dispatch_uid="clean_up_after_document_deletion",
)
def clean_up_after_document_deletion(
    sender: type[Document],
    instance: Document,
    **kwargs,
):
    os.remove(os.path.join(settings.MEDIA_ROOT, instance.docfile.name))

In your appname/apps.py file, make sure to import signal_callbacks, like this:

from django.apps import AppConfig

class AppNameConfig(AppConfig):
    def ready(self):
        from . import signal_callbacks

P.S. The post-delete Django Signals API hasn't changed since Django 1, so it should work as is for all versions of Django, from 1 to 5 (currently, the latest).

Sublet answered 21/1, 2024 at 18:23 Comment(0)
U
0

here is another solution

def delete(self, *args, **kwargs):
   os.remove(os.path.join(settings.MEDIA_ROOT, self.qr_code.name))
   super().delete(*args, **kwargs)

Uziel answered 18/9, 2020 at 8:38 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.