Images¶
Overview¶
The image system is built on top of Wagtail's image infrastructure and extends it with:
- A custom image model (
CustomImage) that stores additional metadata such as transcriptions, translations, and copyright information. - A custom serializer (
ImageSerializer/DetailedImageSerializer) that generates multiple format renditions (JPEG, WebP, and optionally others) in a single API field. - A standalone
image_generatorfunction that can be called directly when the serializer isn't convenient. - A custom
APIImageChooserBlockfor use in StreamField blocks. - A dedicated
/imagesAPI endpoint that exposes the image library.
The CustomImage model¶
app/images/models.py
Extends Wagtail's AbstractImage with the following extra fields:
| Field | Type | Notes |
|---|---|---|
uuid |
UUIDField |
Auto-generated, unique, read-only. Used as the lookup key in the /images endpoint. |
title |
CharField |
Descriptive name. Shown in highlights galleries. |
description |
CharField |
Alt text for the image. |
copyright |
RichTextField |
Credit for images not owned by TNA. Do not include the copyright symbol. |
transcription_heading |
CharField |
Choice of "Transcript" or "Partial transcript". |
transcription |
RichTextField |
Optional text transcription of the image content. |
translation_heading |
CharField |
Choice of "Translation" or "Modern English". |
translation |
RichTextField |
Optional English translation of the transcription. |
alternative_format_heading |
CharField |
Choice of heading for alternative format content. Current option is "Transcript with tables". |
alternative_format |
FileField |
Optional uploaded spreadsheet-style file. Stored under images/alternative_formats/. |
alternative_format accepts only .csv, .xlsx, and .xls files via model validation.
In the Wagtail admin image form, the file chooser is configured with an accept attribute (.csv,.xlsx,.xls) via CustomImageAdminForm (app/images/forms.py). This helps filter selectable files in the local file dialog, while model validation remains the enforcement layer.
The companion model CustomImageRendition extends AbstractRendition and adds a full_url property.
Serializers¶
app/core/serializers/images.py
ImageSerializer¶
The primary serializer for images in API responses. It generates JPEG and WebP renditions in a single field, rather than requiring separate API calls or multiple ImageRenditionFields.
Parameters
| Parameter | Default | Description |
|---|---|---|
rendition_size |
"fill-600x400" |
Wagtail rendition size string applied to the primary rendition. |
jpeg_quality |
60 |
JPEG compression quality (1–100) for the primary rendition. |
webp_quality |
70 |
WebP compression quality (1–100) for the primary rendition. |
background_colour |
"fff" |
Hex colour (without #) used to fill transparent areas. |
formats |
["jpeg", "webp"] |
List of output formats. Any Wagtail-supported format string can be added (e.g. "png"). |
additional_rendition_specs |
None |
Dict of named extra renditions. See Additional renditions below. |
Usage
from wagtail.api import APIField
from app.core.serializers import ImageSerializer
api_fields = [
APIField("image", serializer=ImageSerializer()),
]
API response shape
"image": {
"id": 1,
"uuid": "a1b2c3d4-...",
"title": "Example image",
"description": "Alt text for the image",
"jpeg": {
"url": "/media/images/example.jpg",
"full_url": "https://example.com/media/images/example.jpg",
"width": 600,
"height": 400
},
"webp": {
"url": "/media/images/example.webp",
"full_url": "https://example.com/media/images/example.webp",
"width": 600,
"height": 400
}
}
DetailedImageSerializer¶
Extends ImageSerializer with additional metadata fields intended for in-page image use (e.g. hero images, article body images), rather than teaser/thumbnail use.
Extra fields added to the response:
| Key | Value |
|---|---|
transcript |
{"heading": "Transcript", "text": "<rich text>"} or null |
translation |
{"heading": "Translation", "text": "<rich text>"} or null |
copyright |
Copyright string or null |
from app.core.serializers import DetailedImageSerializer
APIField(
"hero_image", serializer=DetailedImageSerializer(rendition_size="fill-1800x720")
)
Additional renditions¶
Both ImageSerializer and APIImageChooserBlock accept an additional_rendition_specs parameter that generates extra named renditions alongside the primary one.
Each key in the dict becomes the prefix of the output key in the API response (suffixed with the format name).
Simple form - size string only¶
Uses the top-level jpeg_quality and webp_quality values.
ImageSerializer(
rendition_size="fill-1800x720",
additional_rendition_specs={
"small": "fill-900x360",
},
)
Response keys produced: jpeg, webp, small_jpeg, small_webp.
Extended form - size with per-rendition quality overrides¶
Pass a dict with a size key and optional jpeg_quality / webp_quality keys to override quality for that specific rendition only. Any quality not specified falls back to the top-level value.
ImageSerializer(
rendition_size="fill-600x400",
jpeg_quality=60,
webp_quality=70,
additional_rendition_specs={
"small": "fill-300x200", # uses default qualities
"large": {
"size": "fill-1200x800",
"jpeg_quality": 85,
"webp_quality": 90,
},
},
)
Response keys produced: jpeg, webp, small_jpeg, small_webp, large_jpeg, large_webp.
The two forms can be mixed freely in the same dict.
image_generator function¶
app/core/serializers/images.py
A standalone function that can be called directly when you need rendition data outside the serializer context (e.g. in the /images API view).
from app.core.serializers.images import image_generator
image_data = image_generator(
original_image=my_image,
rendition_size="max-900x900",
jpeg_quality=60,
webp_quality=70,
background_colour="fff",
formats=["jpeg", "webp"],
additional_rendition_specs={
"thumb": {"size": "fill-128x128", "jpeg_quality": 50},
},
)
Returns a dict of rendition dicts keyed by format/spec name, or None if the image is falsy or no renditions were produced. All renditions are fetched in a single get_renditions() call for efficiency.
image_generator additional formats¶
When required, the image_generator function (and users of this function) can be passed a formats parameter (list[]). For example, ["jpeg", "webp", "png"] - this will generate jpeg, webp, and png formats of an image in the same representation.
APIImageChooserBlock¶
app/core/blocks/image.py
A Wagtail ImageChooserBlock subclass for use in StreamField block definitions. Instead of returning a raw image ID, get_api_representation returns the same structured dict as DetailedImageSerializer.
Parameters
| Parameter | Default | Description |
|---|---|---|
rendition_size |
"fill-600x400" |
Wagtail rendition size string. |
jpeg_quality |
60 |
JPEG quality for the primary rendition. |
webp_quality |
70 |
WebP quality for the primary rendition. |
background_colour |
"fff" |
Background fill colour (hex, no #). |
additional_rendition_specs |
None |
Named extra renditions - same format as ImageSerializer. |
from app.core.blocks.image import APIImageChooserBlock
image = APIImageChooserBlock(
rendition_size="fill-1800x720",
additional_rendition_specs={"small": "fill-900x360"},
)
/images API endpoint¶
app/api/urls/images.py
Exposes the image library at /api/v2/images/. Images are looked up by uuid rather than the Wagtail default integer id.
The endpoint uses ViewSetImageSerializer, which inherits from Wagtail's built-in ImageSerializer and injects rendition data via image_generator. Default rendition settings:
| Setting | Value |
|---|---|
rendition_size |
"max-900x900" |
jpeg_quality |
60 |
webp_quality |
70 |
background_colour |
"fff" |
Access can be restricted to authenticated API token holders via the WAGTAILAPI_AUTHENTICATION setting.
Extra fields exposed in the response: uuid, title, copyright, tags, transcription_heading, transcription, translation_heading, translation, alternative_format_heading, alternative_format, description, plus the standard jpeg / webp rendition objects.