Rotate image in Django when saved in a model
Giovanni Cortés Jul 12
This is my first post here and I write it more than anything because it was difficult for me to find an answer on the internet. The post already exists in Spanish, so this is just a translation of what I wrote.
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 image 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 rotate_image class Profile(models.Model): user = models.ForeignKey(User) avatar = models.ImageField( verbose_name="Avatar", upload_to=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 to 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.
You have to modify
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 rotate_image class Profile(models.Model): user = models.ForeignKey(User) avatar = models.ImageField( verbose_name="Avatar", upload_to=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