migcontrol / django-migcontrol

A Wagtail-based Django website for Migration Control
GNU General Public License v3.0
0 stars 5 forks source link

Turning Wiki page content from RichTextField to StreamField #268

Open benjaoming opened 5 months ago

benjaoming commented 5 months ago

Currently, there seems to be no real benefit, but we might be able to create a benefit in the future.

Here's a migration that gets it done:

import json

from django.contrib.contenttypes.models import ContentType
from django.core.serializers.json import DjangoJSONEncoder
from django.db import migrations

import wagtail.blocks
import wagtail.fields

def page_to_streamfield(page):
    changed = False
    try:
        json.loads(page.description)
    except ValueError:
        page.description = json.dumps(
            [{"type": "rich_text", "value": page.description}],
        )
        changed = True
    else:
        # It's already valid JSON. Leave it.
        pass

    return page, changed

def pagerevision_to_streamfield(revision_data):
    changed = False
    description = revision_data.get("description")
    if description:
        try:
            json.loads(description)
        except ValueError:
            revision_data["description"] = json.dumps(
                [{
                    "value": description,
                    "type": "rich_text"
                }],
                cls=DjangoJSONEncoder)
            changed = True
        else:
            # It's already valid JSON. Leave it.
            pass
    return revision_data, changed

def page_to_richtext(page):
    changed = False
    if page.description:
        try:
            description_data = json.loads(page.description)
        except ValueError:
            # It's not apparently a StreamField. Leave it.
            pass
        else:
            page.description = "".join([
                child["value"] for child in description_data
                if child["type"] == "rich_text"
            ])
            changed = True

    return page, changed

def pagerevision_to_richtext(revision_data):
    changed = False
    description = revision_data.get("description", "definitely non-JSON string")
    if description:
        try:
            description_data = json.loads(description)
        except ValueError:
            # It's not apparently a StreamField. Leave it.
            pass
        else:
            raw_text = "".join([
                child["value"] for child in description_data
                if child["type"] == "rich_text"
            ])
            revision_data["body"] = raw_text
            changed = True
    return revision_data, changed

def convert(apps, schema_editor, page_converter, pagerevision_converter):
    WikiPage = apps.get_model("wiki", "WikiPage")
    content_type = ContentType.objects.get_for_model(WikiPage)
    Revision = apps.get_model("wagtailcore", "Revision")

    for page in WikiPage.objects.all():

        page, changed = page_converter(page)
        if changed:
            page.save()

        for revision in Revision.objects.filter(
            content_type_id=content_type.pk, object_id=page.pk
        ):
            revision_data = revision.content
            revision_data, changed = pagerevision_converter(revision_data)
            if changed:
                revision.content = revision_data
                revision.save()

def convert_to_streamfield(apps, schema_editor):
    return convert(apps, schema_editor, page_to_streamfield, pagerevision_to_streamfield)

def convert_to_richtext(apps, schema_editor):
    return convert(apps, schema_editor, page_to_richtext, pagerevision_to_richtext)

class Migration(migrations.Migration):

    dependencies = [
        # leave the dependency line from the generated migration intact!
        ("wiki", "0005_alter_wikiindexpage_body"),
        ("wagtailcore", "0076_modellogentry_revision"),
    ]

    operations = [
        migrations.RunPython(
            convert_to_streamfield,
            convert_to_richtext,
        ),

        # leave the generated AlterField intact!
        migrations.AlterField(
            model_name="WikiPage",
            name="description",
            field=wagtail.fields.StreamField(
                [("rich_text", wagtail.blocks.RichTextBlock())],
            ),
        ),
    ]