Django uploading image when creating an instance – post_save signal

This blog post is mostly a way for me to remember how I managed to solve the problem of moving images to a specific folder when creating a new instance by using Django’s post_save signal but hopefully it will help someone else as well.

The problem is that I wanted uploaded images to be saved in a directory containing the id of the instance and in Django the images are saved before the instance gets an id.
This is where signals and especially post_save comes in useful.

My model (simplified and with added line numbers) looks like this:

1 class Item(models.Model):
2    name=models.CharField('name')
3    def image_directory_path(instance,filename):
4        return 'images/item_{0}/image/{1}'.format(instance.id,filename)
5    image=models.ImageField(upload_to=image_directory_path)

So when creating a new instance with an image called profile.jpg Django will upload the image to the directory specified on line 4 which will be resolved to /images/item_None/image/profile.jpg and after that the new instance gets its ID (let’s assume ID 1 for the rest of the post).

Here is where post_save comes in. I use a signals.py file just to keep everything tidy.

 
1 from .models import Item
2 from django.db.models.signals import post_save
3 from django.conf import settings
4 import os
5  
6 @receiver(post_save, sender=Item)
7 def update_image_path(sender, instance, created,**kwargs):
8  if created:
9    imagefile=instance.image
10    
old_name=imagefile.name
11       if not old_name:
12         return
13    new_name = os.path.basename(old_name)
14    new_path = os.path.join(settings.MEDIA_ROOT,sender.image_directory_path(instance, new_name))
15    if not os.path.exists(new_path):
16      os.makedirs(os.path.dirname(new_path))
17    os.rename(imagefile.path, new_path)
18    instance.image.name=sender.image_directory_path(instance, new_name)
19    instance.save()

 

This code gets run each time an Item gets saved, so first, we check if it’s an Item that is created (line 8) which is the only case when we want to run our code.
We first save the image and its name in some temporary variables and check if there even is an image. If there isn’t we just return out of the function.
The new “name” (line 13) is just simply the name of the image (profile.jpg) which we get by removing the directory structure from /images/item_None/image/profile.jpg.
Then (line 14) we create the the new_path with our MEDIA_ROOT and the image_directory_path from our model because now the instance has an id. Which will give us /images/item_1/image/profile.jpg
We need however to make sure the directory structure exists or create it, which we do on line 15 and 16, before moving the image file on the server.
os.rename (line 17) does the moving of the file on the server and then we update the instance so that it points to the right file (line 18) and save the instance (line 19).

Let me know if that was useful for you and if you would like to see more post about technical web development stuff.

One Reply to “Django uploading image when creating an instance – post_save signal”

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.