Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Getting the path of an uploaded media file that uses "upload_to" in django

Tags:

python

django

I am trying to replace the images that will be uploaded for a certain ImageField in my models. Now My models are like this:

class Image(models.Model):
    image = models.ImageField(upload_to='images/')

    def save(self, *args, **kwargs):
         # some resizing here which works fine

As you can see, my model saves the file to the 'images/' directory (because i have different image types that need to go in different sub-directories of /media) which will eventually become '/media/images/' due to this setting in my settings.py:

MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')

Now the problem is where I'm using the receiver method to delete the previously uploaded image.

@receiver(pre_save, sender=Image)
def file_update(sender, **kwargs):
    instance = kwargs['instance']
    print(instance.image.path) ### HERE IS THE PROBLEM
    if instance.image:
        path = instance.image.path ### HERE IS THE PROBLEM
        os.remove(path)

It's supposed to return this:

/home/.../media/images/file.jpg

But it returns this:

/home/.../media/file.jpg

Which obviously results in a No such file or directory error. What am I missing?

A simple hack would be to do something like this:

path = instance.image.path
name = instance.image.name
correct_path = path.replace(name, 'images/' + name)

But that doesn't answer the question of why it happened or what's the correct way of doing it.


UPDATE: In case someone is having the same problem, I tried another approach. First, I fetch the object with it's id and then get that path:

instance = kwargs['instance']
if instance.id is not None:
    current_image = Image.objects.filter(id=instance.id)[0]
    print(current_image.image.path) ### WORKS FINE
    os.remove(current_image.image.path) ### WORKS FINE

This approach has two benefits:

  1. The path will be correct.
  2. Replacing the image is guaranteed to work because we are getting the path of previously saved object not calculating based on the newly submitted object (which if not existing, can lead to problems of not having an ID yet).

The only downside is an extra database query.

like image 845
Mazhar Zandsalimi Avatar asked Dec 29 '19 17:12

Mazhar Zandsalimi


People also ask

How do I get the full path of an uploaded file in Python?

To get current file's full path, you can use the os. path. abspath function. If you want only the directory path, you can call os.

Where does Django save uploaded files?

About the FileField upload_to Parameter Note the upload_to parameter. The files will be automatically uploaded to MEDIA_ROOT/documents/ . A file uploaded today would be uploaded to MEDIA_ROOT/documents/2016/08/01/ . The upload_to can also be a callable that returns a string.

How do I access Django media files?

MEDIA_URL Setting Just below the MEDIA_ROOT setting add the following code to the end of settings.py file. Download this image and save it as python. png in the media directory. To access the file visit http://127.0.0.1:8000/media/python.png .


Video Answer


1 Answers

As Abhyudai indicated: Your 'problem' only occurs in the pre_save. In the post_save you are getting the correct path. I suspect that the correct 'path' is only generated when the file is actually saved (before saving the model it has not been written to the final destination.

However, you can still access the required data in your pre_save:

Absolute path of your media dir:

instance.image.storage.base_location # /home/.../media

The relative path of your upload_to:

instance.image.field.upload_to # images/

Note the .field as we want the field and not the FieldFile normally returned

And of course the name of your file

instance.image.name

Joining all these will give you the path you need to check and delete the existing file, if required

like image 61
Chris Avatar answered Oct 19 '22 19:10

Chris