A Django application designed to optimize your image handling process.
It aids in the creation of HTML <picture>
elements by processing Django images through one or more filters.
django-picture manages different image sizes and types, ensuring that the most appropriate image is served based on the user's device and screen resolution. This improves page load times and also reduces the amount of data that needs to be transferred.
By default, images are generated in a pipeline, out-of-band from the request. When a <picture>
is created, processed images that don't already exist are queued for processing (if they haven't been already) and left out of the HTML.
To install django-picture, simply run the following command:
pip install django-picture
Once installed, add picture
to your INSTALLED_APPS
in your Django settings file:
INSTALLED_APPS = [
# ...
"picture",
# ...
]
Since this uses pyvips, you'll need to have the libvips library installed on your system. In Ubuntu, you can install it with the following command:
sudo apt-get install --no-install-recommends libvips
There are two ways to use django-picture: through a Python class or through a template filter.
The Picture
class is used to create a generator for HTML <picture>
elements. Here's an example of how to use it:
from picture import Picture
thumb = Picture(width="md")
thumb(profile.photo, alt=f"Profile photo for {{ profile.name }}").as_html()
As you can see, thumb
is a Picture
instance that is used to generate an HTML <picture>
element for the profile.photo
image.
In a following section you can see all the different options that you can pass to the Picture
class.
A final optional argument when calling a Picture
instance is build
. This argument specifies whether the image should be processed inline or not. Valid values are:
None
(default): The image will be processed out-of-band from the request."base"
: The base image will be processed inline, but the sources will be processed out-of-band from the request."all"
: The base image and the sources will be processed inline.
The picture
template tag is another way to generate an HTML <picture>
element.
{% load picture %}
{% picture report.image width="md" alt="" %}
You can also pass a Picture
instance to the picture
template tag:
{% load picture %}
{% picture report.image thumb alt="" %}
The template tag does not process images inline.
Whenever a picture is requested, any missing images will be queued for processing and excluded from the HTML.
To build the images in this queue, you can either:
- run the
process_picture_queue
management command (usually in a cron job), or - process it in a task using celery or another task runner (probably using the
queued_picture signal
_).
The Picture
class and the picture
template tag can be called with the following options.
width
Limit the width of the image. Either use an integer, or one of the following tailwind sizes as a string: "xs", "sm", "md", "lg", "screen-sm", "screen-md", "screen-lg", "screen-xl" or "screen-2xl"
ratio
The aspect ratio of the image to generate.
Use a float representing the ratio (e.g. 4/5
) or one of the following strings: "square", "video" (meaning 16/9), "video_vertical", "golden" (using the golden ratio), "golden_vertical".
The default is "video"
(16/9).
crop
Whether to crop the image.
The default is True
.
Use a boolean, or tuple of two floats, or the comma separated string equivalent. True
is replaced with to (0.5, 0.5)
meaning the image is cropped from the center. The numbers are percentages of the image size.
You can also use the following keywords: tl
(top left), tr
(top right), bl
(bottom left), br
(bottom right), and t
or b
. This will set the percentage to 0 or 100 for the appropriate axis.
If crop is False
, the image will be resized so that it will cover the requested ratio but not cropped down. This is useful when you want to handle the cropping in CSS using object-fit
.
focal_window
A focal window to zoom in on when shrinking the image. Use a tuple of four floats (or a comma separated string equivalent) where the first pair of percentages is the top left corner and the second pair of percentages is the bottom right corner.
quality
The quality of the image. For example, quality=90
means that the image will be compressed with a quality of 90. The default is 80.
alternatives
A list of alternative versions of the image. Use a dictionary to specify different picture options for each of these alternatives, for example alternatives={"100w": {"width": 100}, "2x": {}}
.
The default is ["2x"]
.
formats
The list of better image formats to ensure are generated and preferentially used. The valid values are "avif"
and "webp"
.
Use a dictionary to specify different picture options for different formats.
The default is {"avif": {"quality": 60}}
.
base
A dictionary of options to use for the base fallback image. If provided, this is used in the fallback <img>
tag rather than the source image.
The default is {"width": "screen-xl", "crop": False}
.
This signal is triggered for each that FileField
that was uncommitted when it's model instance is saved.
It can be used to build & pre-queue images for a model instance. For example:
from django.db import models
from django.db.models.fields.files import ImageFieldFile
from my_app.pictures import thumbnail
def generate_images(instance, image, **kwargs):
"""
Trigger generation of all related `<picture>` images for an uncommitted ImageField
just before its model is saved.
"""
if isinstance(image, ImageFieldFile):
thumbnail(image, build="base")
In your apps.py
file, connect this receiver:
from django.apps import AppConfig
from picture.signals import file_post_save
class MyAppConfig(AppConfig):
name = 'my_app'
def ready(self):
from my_app.signals import generate_images
file_post_save.connect(generate_images)
This signal is triggered whenever an image is missing and was not already queued for building.
It can be used to process the queue in a task using celery or another task runner. Here's an example tasks.py
:
from picture.management import process_queue
@app.task
def process_picture_queue():
process_queue()
In your apps apps.py
file, connect this receiver:
In your `apps.py` file, connect your `process_picture_queue` function to the `queued_picture` signal to process the queue whenever an image is saved:
```python
from django.apps import AppConfig
from picture.signals import queued_picture
class MyAppConfig(AppConfig):
name = 'my_app'
def ready(self):
from my_app.tasks import process_picture_queue
queued_picture.connect(process_picture_queue.delay)