1

TLDR: Getting error: LookupError: No installed app with label 'wagtailimages'. once the custom data migration is executed in wagtail which causes all tests to fail, as Django can't find the app after running the latest migration.

I needed to add a few custom fields to my image model in my wagtail installation that supports a Vue SPA.

I followed the guidelines in the docs here: http://docs.wagtail.io/en/v2.0/advanced_topics/images/custom_image_model.html

So, I created a custom image model along with custom rendition like this:

class CustomImage(AbstractImage):
    alt_text = models.CharField(max_length=255, blank=True)
    caption = models.CharField(max_length=255, blank=True)

    admin_form_fields = Image.admin_form_fields + (
        "alt_text",
        "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"),
        )

I also changed the WAGTAILIMAGES_IMAGE_MODEL setting to point to my new model:

WAGTAILIMAGES_IMAGE_MODEL = "pages.CustomImage"

I wrote a data migration with the help of this blog post which refers to this StackOverflow discussion:

# Generated by Django 2.1.10 on 2020-01-15 09:03

from django.db import migrations, models


def forwards_func(apps, schema_editor):
    wagtail_image_model = apps.get_model("wagtailimages", "Image")
    custom_image_model = apps.get_model("pages", "CustomImage")
    tagged_item_model = apps.get_model("taggit", "TaggedItem")
    django_content_type = apps.get_model("contenttypes", "contenttype")

    db_alias = schema_editor.connection.alias

    # Get images stored in default wagtail image model
    images = wagtail_image_model.objects.using(db_alias).all()
    new_images = []
    for image in images:
        new_images.append(
            custom_image_model(
                id=image.id,
                title=image.title,
                file=image.file,
                width=image.width,
                height=image.height,
                created_at=image.created_at,
                focal_point_x=image.focal_point_x,
                focal_point_y=image.focal_point_y,
                focal_point_width=image.focal_point_width,
                focal_point_height=image.focal_point_height,
                file_size=image.file_size,
                collection=image.collection,
                uploaded_by_user=image.uploaded_by_user,
            )
        )

    # Create images in new model
    custom_image_model.objects.using(db_alias).bulk_create(new_images)
    # Leave all images in previous model untouched.

    # Move tags from old image to new image model. Moving tags is
    # a little different case. The lookup table taggit_taggeditem looks like this:
    # id   object_id   content_type_id   tag_id
    # 1    1           10                 1
    # 2    1           10                 2
    # 3    1           10                 3
    # 4    1           10                 4
    # In our case, the object_id will be same for old and new image model
    # objects. So, we have to only change the content_type_id
    ct_custom_img_model, created = django_content_type.objects.using(
        db_alias
    ).get_or_create(app_label="pages", model="customimage")
    ct_wagtail_model = django_content_type.objects.using(db_alias).get(
        app_label="wagtailimages", model="image"
    )

    tagged_item_model.objects.using(db_alias).filter(
        content_type_id=ct_wagtail_model.id
    ).update(content_type_id=ct_custom_img_model.id)


def reverse_func(apps, schema_editor):
    # We get the model from the versioned app registry;
    custom_image_model = apps.get_model("pages", "CustomImage")
    tagged_item_model = apps.get_model("taggit", "TaggedItem")
    django_content_type = apps.get_model("contenttypes", "contenttype")

    db_alias = schema_editor.connection.alias

    # Move tags from new image model to old wagtail model
    ct_extended_model = django_content_type.objects.using(db_alias).get(
        app_label="pages", model="customimage"
    )
    ct_wagtail_model = django_content_type.objects.using(db_alias).get(
        app_label="wagtailimages", model="image"
    )

    tagged_item_model.objects.using(db_alias).filter(
        content_type_id=ct_extended_model.id
    ).update(content_type_id=ct_wagtail_model.id)

    # Delete all images created in the new model
    custom_image_model.objects.using(db_alias).all().delete()


class Migration(migrations.Migration):

    dependencies = [
        ("pages", "0030_auto_20200115_0817"),
    ]

    operations = [
        migrations.RunPython(forwards_func, reverse_func),
    ]

The forward migrations work as expected to migrate all the data and work well when I tested these changes locally.

I tried to test my backward migration, and they also work fine.

However, if I try to get the old wagtailimages.Image model in my reverse_func function, it throws an error LookupError: No installed app with label 'wagtailimages'.

Although I actually wanted to delete the images from the old model just to perform cleanup, but due to this error, I thought that it is not that important and I can just move on.

Unfortunately, as soon as I pushed the code to CI, all my tests were failing as when this 31st migration which is a custom data migration is applied, Django seems to not find the wagtailimages app at all.

I'm not sure what is the issue here. I've been trying to debug this issue for a while now but all my efforts were futile. I also didn't find anything related to this on the web which might help.

I've also tried to simplify my migration like not doing barely anything at all and just trying to fetch the model using Django's apps.get_model. The forward migration works fine, but in reverse migration, it seems that wagtailimages app just vanishes. I'm not sure why django.setup() isn't able to load that app.

Can anyone help in this regard and provide me a pointer on where are things going sideways?

Sanyam Khurana
  • 1,336
  • 1
  • 14
  • 27

2 Answers2

0

wagtail.wagtailimages is now wagtail.images

Reference (see Old Name/New Name table)

Put wagtail.images in INSTALLED_APPS.

Dan Swain
  • 2,910
  • 1
  • 16
  • 36
  • the app config inside `wagtail.images` refers to label `wagtailimages`. But where are you referring, that I should make a change? – Sanyam Khurana Jan 17 '20 at 13:43
  • The "label" of the app is still `wagtailimages`. Only the module path has changed to `wagtail.images`. That means references to modes use e.g. `wagtailimages.Image`, but you do `from wagtail.images.models import Image`. – tbrlpld Jun 19 '22 at 03:10
0

I just ran into this myself. Your migration seems to depend on models from other apps, wagtailimages is among them. You gonna want to list the labels of these apps with a migration (the latest when creating your datamigration) in the dependencies list for the migration.

You gonna need to find a migration name for each of the apps.

    dependencies = [
        ("pages", "0030_auto_20200115_0817"),
        ("wagtailimages", "0023_add_choose_permissions"),  # I am using Wgatail 2.16 here.
        ...
    ]

This exact error is actually explained in the Django docs.

tbrlpld
  • 776
  • 6
  • 16