Convert Existing Site to Use a Custom Image Model

Added in version 0.19: Added support for custom image models. You must be on version 0.19 or higher in order to follow this guide.

Using a custom image model is a very similar process to using a custom user model in Django — it is easy to do when starting a new project but extremely difficult to do mid-project. This process requires deep understanding of the Django ORM, SQL, and relational database design. It is therefore recommended to truly evaluate if and why you really need to switch to a custom image model. If you’re simply looking to store metadata about an image, the same effect could be much more easily achieved with a separate “metadata” model with a OneToOne relationship to the Image model, and do a reverse lookup (e.g. image.metadata).

Before starting this guide, ensure you have updated to the latest Wagtail CRX, have run all migrations, and do not have any pending migrations.

$ python manage.py migrate
$ python manage.py makemigrations
No changes detected

Step 1: Create a custom image model in a new app

It is imperative that the custom image model lives in a Django app which does not rely on or import coderedcms. It is recommended to create a separate “pure” app to contain custom image and document models for your site. Failure to separate the custom image model will create a circular dependency issue in migrations.

Create an empty Django app, ours will be named mediamodels:

$ django-admin startapp mediamodels

In mediamodels/models.py, add your custom image model code, following the Wagtail custom image sample code:

# models.py

from django.db import models
from wagtail.images.models import Image, AbstractImage, AbstractRendition


class CustomImage(AbstractImage):
    # Add any extra fields to image here

    # eg. To add a caption field:
    # caption = models.CharField(max_length=255, blank=True)

    admin_form_fields = Image.admin_form_fields + (
        # Then add the field names here to make them appear in the form:
        # 'caption',
    )


class CustomRendition(AbstractRendition):
    image = models.ForeignKey(CustomImage, on_delete=models.CASCADE, related_name='renditions')

    class Meta:
        unique_together = (
            ('image', 'filter_spec', 'focal_point_key'),
        )

Step 2: Make migrations

Before switching your project to the new custom model, first make a migration for this model. If your custom image model already exists and has already been migrated, you can skip this step.

$ python manage.py makemigrations mediamodels

Now, apply the migration:

$ python manage.py migrate mediamodels

Step 3: Switch to the new image model

In your Django settings file, (probably under settings/base.py) set the WAGTAILIMAGES_IMAGE_MODEL setting to point to it:

WAGTAILIMAGES_IMAGE_MODEL = "mediamodels.CustomImage"

Step 4: Migrate your models and database schema by hand

At this point the database tables of existing coderedcms models have FK pointing to wagtailimages.Image, however Django thinks they are pointing to the new custom image table, hence creating FOREIGN KEY constraint problems.

For this same reason, running makemigrations will yield “No changes detected” as the Django ORM has no knowledge that the foreign keys are pointing to the wrong tables. Hence the database schema must be changed by hand.

The end result is your existing image database tables should be moved to the new custom image table, and every current table with a foreign key to the old image table needs to be updated as a foreign key to the new image table.

This process will differ from project to project, so you will need to find your own way to update the database schema that fits your project. Many related discussions about switching the Django User model (replace “user” with “image” in this context) can be found online and are highly relevant and helpful. Start by reading Django ticket #25313 on the subject.

To help with your database update, below is a list of each concrete CodeRed model which references the Image. A search query of the source code can also yield specific results.

  • coderedcms.models.CoderedPage.cover_image

  • coderedcms.models.CoderedPage.og_image

  • coderedcms.models.CoderedPage.struct_org_logo

  • coderedcms.models.CoderedPage.struct_org_image

  • coderedcms.models.CarouselSlide.image

  • coderedcms.models.LayoutSettings.logo

  • coderedcms.models.LayoutSettings.favicon