Serve Scaled Images With Django Rest Framework and Django ImageKit
To achieve optimal speed for web apps and web sites its important to minimise file size at almost all cost.
Usually the biggest culprit is images (Apart from video of course), Images can easily add up to 90% of the total load your application if your are not careful.
You can mitigate this by allowing people to upload files with a maximum file size or automatically reduce the quality of the image. But that will create an obstacle between the user and your application.
We all know how irritating it is when a form we fill out or an image we upload fail due to some constraints.
Its better to allow you user to upload images of any shape and size and handle the resizing, optimisation and formatting automatically on the backend so that you user can enjoy a smooth and frictionless experience.
In this case we are working with a Django REST Framework API, and make use of a fantastic package called Django ImageKit to do all the heavy lifting for us.
Django ImageKit takes the originally uploaded image and create extra specified sizes of that same images and automatically creates cached versions of them.
Enough of the talking, let's get started.
Install Django ImageKit
Install the package via command line in your activated project environment with pip.
pip install django-imagekit
Add django-imagekit to you installed apps in settings.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'django.contrib.sites',
'rest_framework',
'rest_framework.authtoken',
'imagekit',
]
Create a Model that use Django ImageKit
To make use of django-imagekit create extra ImageSpecField's and specify size, format and quality.
from imagekit.models import ImageSpecField
from imagekit.processors import ResizeToFill, ResizeToFill
class Article(models.Model):
created_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True)
updated_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True, blank=True, related_name="+")
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
title = models.CharField(max_length=50)
slug = AutoSlugField(populate_from='title', unique=True, max_length=50)
image = models.ImageField(upload_to='topic', null=True, blank=True)
image_large = ImageSpecField(source='image', processors=[ResizeToFill(512, 512)], format='PNG', options={'quality': 70})
image_medium = ImageSpecField(source='image', processors=[ResizeToFill(256, 256)], format='PNG', options={'quality': 70})
image_small = ImageSpecField(source='image', processors=[ResizeToFill(128, 128)], format='PNG', options={'quality': 70})
image_tag = ImageSpecField(source='image', processors=[ResizeToFill(28, 28)], format='PNG', options={'quality': 70})
description = RichTextField(max_length=2000, null=True, blank=True)
content = RichTextField(max_length=10000, null=True, blank=True)
class Meta:
verbose_name = 'Article'
verbose_name_plural = 'Articles'
Include the Cached and Resized Images in Your Serializer
A common issue when using this approach is that people don't see there images represented in the JSON output of the browsable API View.
To output the resized and cached images in your serializer you need to load it with a regular ImageField as following.
class ArticleSerializer(serializers.ModelSerializer):
image_large = serializers.ImageField(read_only=True)
image_medium = serializers.ImageField(read_only=True)
image_small = serializers.ImageField(read_only=True)
image_tag = serializers.ImageField(read_only=True)
class Meta:
model = Topic
fields = [
'created_at',
'updated_at',
'created_by',
'title',
'slug',
'image',
'image_large',
'image_medium',
'image_small',
'image_tag',
'description',
]
As you can see we specified each of the django-imagekit fields as regular ImageFields in the serializer. Without them your formatted images wouldn't be visible.
Your serializer should now output the images according to your specification.
{
"created_at": "2020-01-08T04:30:35.298160Z",
"updated_at": "2020-01-08T04:30:35.298188Z",
"created_by": 1,
"title": "An Awesome Article",
"slug": "an-awesome-article",
"image": "https://images.somedomain.com/articles/awesome-image.png",
"image_large": "https://images.somedomain.com/CACHE/articles/5326a90dafc79cc7e6c5948660299d7b.png",
"image_medium": "https://images.somedomain.com/CACHE/articles/2903482490kdc7e6c5948660299d7b.png",
"image_small": "https://images.somedomain.com/CACHE/articles/8834dafc79cc7e6c5948660299d7b.png",
"image_tag": "https://images.somedomain.com/CACHE/articles/5326a90dafc79cc7e6c59asdsd9d7b.png",
"description": "An absolutely awesome description for an awesome article"
}
Now you can with ease fetch just the sizes you seem fit for a particular component you are building, or for any use-case really. That's perfect for serving thumbnails and images that adhere your design choices without compromising on quality of the image or speed of your application.
I hope you find the tutorial useful, and if you have any questions or input, just let me know in the comments below.