Rotate image in Django when saved in a model

Giovanni Cortes
3 min readJan 10, 2018

--

There is a bug with browsers that when you take a picture with the cell phone frontally and want to upload it to a form, for example for your profile, the image is flipped

I was investigating a bit and it’s mostly because there is information in the images that makes the browsers do not show it correctly, a little more information can be found in a Confluence thread. So you have to fix it on the server side so we can see the image correctly.

First of all, I am going to assume that we are using Pillow as the image library, since it is the most used.

Fixing the bug

First, what we are going to do is create a small function that will help us to rotate the image with the help of Pillow.

from PIL import Image, ExifTags

def rotate_image(filepath):
try:
image = Image.open(filepath)
for orientation in ExifTags.TAGS.keys():
if ExifTags.TAGS[orientation] == 'Orientation':
break
exif = dict(image._getexif().items())

if exif[orientation] == 3:
image = image.rotate(180, expand=True)
elif exif[orientation] == 6:
image = image.rotate(270, expand=True)
elif exif[orientation] == 8:
image = image.rotate(90, expand=True)
image.save(filepath)
image.close()
except (AttributeError, KeyError, IndexError):
# cases: image don't have getexif
pass

If you want to know why these numbers, here more and better information about the orientation of EXIF https://www.impulseadventure.com/photo/exif-orientation.html

That small function can be useful for other projects that we are doing, but in the case of Django, we need to do some more things to apply it when the image is saved.

In this example, we are going to have a model called Profile, which is where we will store our image

from django.db import models
from utilities.utils import get_filename, rotate_image

class Profile(models.Model):
user = models.ForeignKey(User)
avatar = models.ImageField(
verbose_name="Avatar",
upload_to=get_upload_path,
blank=True,
null=True
)
...

Once we have our model, we need a way to use our function to process the image once the image has been saved, in order to bring the path as it was saved and not have errors.

For that, we are going to make use of the signals that Django has, especially post_save, since we have to process the image once it has been saved in the file system.

We have to modify our models.py file to bring the necessary libraries

import os

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

from utilities.utils import get_filename, rotate_image

class Profile(models.Model):
user = models.ForeignKey(User)
avatar = models.ImageField(
verbose_name="Avatar",
upload_to=get_upload_path,
blank=True,
null=True
)
...

@receiver(post_save, sender=Profile, dispatch_uid="update_image_profile")
def update_image(sender, instance, **kwargs):
if instance.image:
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
fullpath = BASE_DIR + instance.image.url
rotate_image(fullpath)

And with this change, when we save an image in Django we can see the image correctly

If you want to see this post in Spanish, you can check my blog

--

--

Giovanni Cortes

iOS & Elixir Engineer. Loves programming in Elixir, Swift and Python.