wagtail / wagtailtrans

A Wagtail add-on for supporting multilingual sites
http://wagtailtrans.readthedocs.io/
BSD 3-Clause "New" or "Revised" License
104 stars 60 forks source link

Migration path to wagtail 2.11 native multi-language support #208

Open ceelian opened 3 years ago

ceelian commented 3 years ago

Is there a migration path for existing wagtail installation which already uses wagtailtrans to wagtails new native multi-language support as it seems more powerful than wagtailtrans (snippet translations).

(As I am not aware to which project it may concern also added this in https://github.com/wagtail/wagtail-localize/issues/297)

ceelian commented 3 years ago

I just saw that wagtailtrans currently does not support version 2.11 anymore (only wagtail<2.11).

ceelian commented 3 years ago

I finally managed to create a migration path. Because it finally needed some more steps as expected I wrote a blog post about it. I also created the necessary fork of wagtailtrans at https://github.com/carrotandcompany/wagtailtrans

If anyone has any suggestions, improvements, bugfixes to the migration path or the migration fork, I am happy to update the migration guide. Hope this is helpful for others as well.

drcongo commented 3 years ago

This is excellent work @ceelian - huge thanks. 🙏

mentecuantica commented 3 years ago

@ceelian Didn't you get an error about 'canonical_page' ? That for example MainPage doesn't have attribute 'canonical_page'?

ceelian commented 3 years ago

@mentecuantica I don't think so. At what step did the error occur?

mentecuantica commented 3 years ago

@ceelian at the step - Make migrations again here is the stacktrace

wagtail@ebdd3e61cbe8:~$ ./manage.py makemigrations
Traceback (most recent call last):
  File "/usr/local/lib/python3.6/site-packages/django/db/models/options.py", line 575, in get_field
    return self.fields_map[field_name]
KeyError: 'canonical_page'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "./manage.py", line 10, in <module>
    execute_from_command_line(sys.argv)
  File "/usr/local/lib/python3.6/site-packages/django/core/management/__init__.py", line 401, in execute_from_command_line
    utility.execute()
  File "/usr/local/lib/python3.6/site-packages/django/core/management/__init__.py", line 395, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/usr/local/lib/python3.6/site-packages/django/core/management/base.py", line 330, in run_from_argv
    self.execute(*args, **cmd_options)
  File "/usr/local/lib/python3.6/site-packages/django/core/management/base.py", line 368, in execute
    self.check()
  File "/usr/local/lib/python3.6/site-packages/django/core/management/base.py", line 396, in check
    databases=databases,
  File "/usr/local/lib/python3.6/site-packages/django/core/checks/registry.py", line 70, in run_checks
    new_errors = check(app_configs=app_configs, databases=databases)
  File "/usr/local/lib/python3.6/site-packages/wagtail/admin/checks.py", line 63, in get_form_class_check
    if not issubclass(edit_handler.get_form_class(), WagtailAdminPageForm):
  File "/usr/local/lib/python3.6/site-packages/wagtail/admin/edit_handlers.py", line 355, in get_form_class
    widgets=self.widget_overrides())
  File "/usr/local/lib/python3.6/site-packages/wagtail/admin/edit_handlers.py", line 263, in widget_overrides
    widgets.update(handler_class.widget_overrides())
  File "/usr/local/lib/python3.6/site-packages/wagtail/admin/edit_handlers.py", line 263, in widget_overrides
    widgets.update(handler_class.widget_overrides())
  File "/usr/local/lib/python3.6/site-packages/wagtail/admin/edit_handlers.py", line 263, in widget_overrides
    widgets.update(handler_class.widget_overrides())
  File "/usr/local/lib/python3.6/site-packages/wagtail/admin/edit_handlers.py", line 606, in widget_overrides
    target_models=self.target_models(),
  File "/usr/local/lib/python3.6/site-packages/wagtail/admin/edit_handlers.py", line 630, in target_models
    return [self.db_field.remote_field.model]
  File "/usr/local/lib/python3.6/site-packages/django/utils/functional.py", line 48, in __get__
    res = instance.__dict__[self.name] = self.func(instance)
  File "/usr/local/lib/python3.6/site-packages/wagtail/admin/edit_handlers.py", line 530, in db_field
    return model._meta.get_field(self.field_name)
  File "/usr/local/lib/python3.6/site-packages/django/db/models/options.py", line 577, in get_field
    raise FieldDoesNotExist("%s has no field named '%s'" % (self.object_name, field_name))
django.core.exceptions.FieldDoesNotExist: MainPage has no field named 'canonical_page'
wagtail@ebdd3e61cbe8:~$ ./manage.py makemigrations
Traceback (most recent call last):
  File "/usr/local/lib/python3.6/site-packages/django/db/models/options.py", line 575, in get_field
    return self.fields_map[field_name]
KeyError: 'canonical_page'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "./manage.py", line 10, in <module>
    execute_from_command_line(sys.argv)
  File "/usr/local/lib/python3.6/site-packages/django/core/management/__init__.py", line 401, in execute_from_command_line
    utility.execute()
  File "/usr/local/lib/python3.6/site-packages/django/core/management/__init__.py", line 395, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/usr/local/lib/python3.6/site-packages/django/core/management/base.py", line 330, in run_from_argv
    self.execute(*args, **cmd_options)
  File "/usr/local/lib/python3.6/site-packages/django/core/management/base.py", line 368, in execute
    self.check()
  File "/usr/local/lib/python3.6/site-packages/django/core/management/base.py", line 396, in check
    databases=databases,
  File "/usr/local/lib/python3.6/site-packages/django/core/checks/registry.py", line 70, in run_checks
    new_errors = check(app_configs=app_configs, databases=databases)
  File "/usr/local/lib/python3.6/site-packages/wagtail/admin/checks.py", line 63, in get_form_class_check
    if not issubclass(edit_handler.get_form_class(), WagtailAdminPageForm):
  File "/usr/local/lib/python3.6/site-packages/wagtail/admin/edit_handlers.py", line 355, in get_form_class
    widgets=self.widget_overrides())
  File "/usr/local/lib/python3.6/site-packages/wagtail/admin/edit_handlers.py", line 263, in widget_overrides
    widgets.update(handler_class.widget_overrides())
  File "/usr/local/lib/python3.6/site-packages/wagtail/admin/edit_handlers.py", line 263, in widget_overrides
    widgets.update(handler_class.widget_overrides())
  File "/usr/local/lib/python3.6/site-packages/wagtail/admin/edit_handlers.py", line 263, in widget_overrides
    widgets.update(handler_class.widget_overrides())
  File "/usr/local/lib/python3.6/site-packages/wagtail/admin/edit_handlers.py", line 606, in widget_overrides
    target_models=self.target_models(),
  File "/usr/local/lib/python3.6/site-packages/wagtail/admin/edit_handlers.py", line 630, in target_models
    return [self.db_field.remote_field.model]
  File "/usr/local/lib/python3.6/site-packages/django/utils/functional.py", line 48, in __get__
    res = instance.__dict__[self.name] = self.func(instance)
  File "/usr/local/lib/python3.6/site-packages/wagtail/admin/edit_handlers.py", line 530, in db_field
    return model._meta.get_field(self.field_name)
  File "/usr/local/lib/python3.6/site-packages/django/db/models/options.py", line 577, in get_field
    raise FieldDoesNotExist("%s has no field named '%s'" % (self.object_name, field_name))
django.core.exceptions.FieldDoesNotExist: MainPage has no field named 'canonical_page'
wagtail@ebdd3e61cbe8:~$ ./manage.py makemigrations
Traceback (most recent call last):
  File "/usr/local/lib/python3.6/site-packages/django/db/models/options.py", line 575, in get_field
    return self.fields_map[field_name]
KeyError: 'canonical_page'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "./manage.py", line 10, in <module>
    execute_from_command_line(sys.argv)
  File "/usr/local/lib/python3.6/site-packages/django/core/management/__init__.py", line 401, in execute_from_command_line
    utility.execute()
  File "/usr/local/lib/python3.6/site-packages/django/core/management/__init__.py", line 395, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/usr/local/lib/python3.6/site-packages/django/core/management/base.py", line 330, in run_from_argv
    self.execute(*args, **cmd_options)
  File "/usr/local/lib/python3.6/site-packages/django/core/management/base.py", line 368, in execute
    self.check()
  File "/usr/local/lib/python3.6/site-packages/django/core/management/base.py", line 396, in check
    databases=databases,
  File "/usr/local/lib/python3.6/site-packages/django/core/checks/registry.py", line 70, in run_checks
    new_errors = check(app_configs=app_configs, databases=databases)
  File "/usr/local/lib/python3.6/site-packages/wagtail/admin/checks.py", line 63, in get_form_class_check
    if not issubclass(edit_handler.get_form_class(), WagtailAdminPageForm):
  File "/usr/local/lib/python3.6/site-packages/wagtail/admin/edit_handlers.py", line 355, in get_form_class
    widgets=self.widget_overrides())
  File "/usr/local/lib/python3.6/site-packages/wagtail/admin/edit_handlers.py", line 263, in widget_overrides
    widgets.update(handler_class.widget_overrides())
  File "/usr/local/lib/python3.6/site-packages/wagtail/admin/edit_handlers.py", line 263, in widget_overrides
    widgets.update(handler_class.widget_overrides())
  File "/usr/local/lib/python3.6/site-packages/wagtail/admin/edit_handlers.py", line 263, in widget_overrides
    widgets.update(handler_class.widget_overrides())
  File "/usr/local/lib/python3.6/site-packages/wagtail/admin/edit_handlers.py", line 606, in widget_overrides
    target_models=self.target_models(),
  File "/usr/local/lib/python3.6/site-packages/wagtail/admin/edit_handlers.py", line 630, in target_models
    return [self.db_field.remote_field.model]
  File "/usr/local/lib/python3.6/site-packages/django/utils/functional.py", line 48, in __get__
    res = instance.__dict__[self.name] = self.func(instance)
  File "/usr/local/lib/python3.6/site-packages/wagtail/admin/edit_handlers.py", line 530, in db_field
    return model._meta.get_field(self.field_name)
  File "/usr/local/lib/python3.6/site-packages/django/db/models/options.py", line 577, in get_field
    raise FieldDoesNotExist("%s has no field named '%s'" % (self.object_name, field_name))
django.core.exceptions.FieldDoesNotExist: MainPage has no field named 'canonical_page'
wagtail@ebdd3e61cbe8:~$ ./manage.py migrate
Traceback (most recent call last):
  File "/usr/local/lib/python3.6/site-packages/django/db/models/options.py", line 575, in get_field
    return self.fields_map[field_name]
KeyError: 'canonical_page'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "./manage.py", line 10, in <module>
    execute_from_command_line(sys.argv)
  File "/usr/local/lib/python3.6/site-packages/django/core/management/__init__.py", line 401, in execute_from_command_line
    utility.execute()
  File "/usr/local/lib/python3.6/site-packages/django/core/management/__init__.py", line 395, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/usr/local/lib/python3.6/site-packages/django/core/management/base.py", line 330, in run_from_argv
    self.execute(*args, **cmd_options)
  File "/usr/local/lib/python3.6/site-packages/django/core/management/base.py", line 371, in execute
    output = self.handle(*args, **options)
  File "/usr/local/lib/python3.6/site-packages/django/core/management/base.py", line 85, in wrapped
    res = handle_func(*args, **kwargs)
  File "/usr/local/lib/python3.6/site-packages/django/core/management/commands/migrate.py", line 75, in handle
    self.check(databases=[database])
  File "/usr/local/lib/python3.6/site-packages/django/core/management/base.py", line 396, in check
    databases=databases,
  File "/usr/local/lib/python3.6/site-packages/django/core/checks/registry.py", line 70, in run_checks
    new_errors = check(app_configs=app_configs, databases=databases)
  File "/usr/local/lib/python3.6/site-packages/wagtail/admin/checks.py", line 63, in get_form_class_check
    if not issubclass(edit_handler.get_form_class(), WagtailAdminPageForm):
  File "/usr/local/lib/python3.6/site-packages/wagtail/admin/edit_handlers.py", line 355, in get_form_class
    widgets=self.widget_overrides())
  File "/usr/local/lib/python3.6/site-packages/wagtail/admin/edit_handlers.py", line 263, in widget_overrides
    widgets.update(handler_class.widget_overrides())
  File "/usr/local/lib/python3.6/site-packages/wagtail/admin/edit_handlers.py", line 263, in widget_overrides
    widgets.update(handler_class.widget_overrides())
  File "/usr/local/lib/python3.6/site-packages/wagtail/admin/edit_handlers.py", line 263, in widget_overrides
    widgets.update(handler_class.widget_overrides())
  File "/usr/local/lib/python3.6/site-packages/wagtail/admin/edit_handlers.py", line 606, in widget_overrides
    target_models=self.target_models(),
  File "/usr/local/lib/python3.6/site-packages/wagtail/admin/edit_handlers.py", line 630, in target_models
    return [self.db_field.remote_field.model]
  File "/usr/local/lib/python3.6/site-packages/django/utils/functional.py", line 48, in __get__
    res = instance.__dict__[self.name] = self.func(instance)
  File "/usr/local/lib/python3.6/site-packages/wagtail/admin/edit_handlers.py", line 530, in db_field
    return model._meta.get_field(self.field_name)
  File "/usr/local/lib/python3.6/site-packages/django/db/models/options.py", line 577, in get_field
    raise FieldDoesNotExist("%s has no field named '%s'" % (self.object_name, field_name))
django.core.exceptions.FieldDoesNotExist: MainPage has no field named 'canonical_page
ceelian commented 3 years ago

canonical_page is part of the TranslatablePage model from wagtailtrans which you just removed in the previous step. To me it looks like either you have some reference to the canonical_page field left in your project's code or you have installed an external package to your project which is relying on that field.

mentecuantica commented 3 years ago

@ceelian I don't have any mention of canonical_page in my code, here is how my requirements.txt looks like

Django==3.1.4
wagtail==2.11.3
gunicorn==20.0.4
psycopg2-binary==2.8.1
wagtailfontawesome==1.2.1
git+git://github.com/carrotandcompany/wagtailtrans@master#egg=wagtailtrans
django-rosetta==0.9.2
social-auth-app-django==3.1.0
git+git://github.com/byashimov/typus.git#egg=typus
google-api-python-client==1.8.0
google-auth-httplib2==0.0.3
google-auth-oauthlib==0.4.1
djangorestframework-recursive==0.1.2
sentry-sdk==0.7.10
wagtail-headless-preview==0.1.4
wagtail-localize==0.9.4

canonical_page

admin_ui

ceelian commented 3 years ago

@mentecuantica have you removed wagtailtrans from your settings?

INSTALLED_APPS = [
    ....
    'wagtailtrans'   # <-- remove this line
    'wagtail_localize',
    'wagtail_localize.locales'
]
mentecuantica commented 3 years ago

@ceelian Nope, i haven't, because this goes much later in your blog

ceelian commented 3 years ago

@mentecuantica can you try that? Because if that works, I will try to improve the blog post.

mentecuantica commented 3 years ago

@ceelian Yes it helped, a bit for me (cause i have a lot of code logic relying on the wagtailtrans)

drcongo commented 3 years ago

@ceelian I've finally gotten around to trying this (I've spotted a couple of missing bits of info in the blog post, let me know if there's a repo somewhere you want me to PR those to), BUT when I actually come to try running the migration I get...

django.core.exceptions.FieldError: Auto-generated field 'page_ptr' in class 'ArticlePage' for parent_link to base class 'Page' clashes with declared field of the same name.

If I dir(ArticlePage) I don't see a declared field called page_ptr, nor do I see one in ArticlePage._meta.fields which outputs...

(<django.db.models.fields.AutoField: id>,
 <django.db.models.fields.CharField: path>,
 <django.db.models.fields.PositiveIntegerField: depth>,
 <django.db.models.fields.PositiveIntegerField: numchild>,
 <django.db.models.fields.CharField: title>,
 <django.db.models.fields.CharField: draft_title>,
 <django.db.models.fields.SlugField: slug>,
 <django.db.models.fields.related.ForeignKey: content_type>,
 <django.db.models.fields.BooleanField: live>,
 <django.db.models.fields.BooleanField: has_unpublished_changes>,
 <django.db.models.fields.TextField: url_path>,
 <django.db.models.fields.related.ForeignKey: owner>,
 <django.db.models.fields.CharField: seo_title>,
 <django.db.models.fields.BooleanField: show_in_menus>,
 <django.db.models.fields.TextField: search_description>,
 <django.db.models.fields.DateTimeField: go_live_at>,
 <django.db.models.fields.DateTimeField: expire_at>,
 <django.db.models.fields.BooleanField: expired>,
 <django.db.models.fields.BooleanField: locked>,
 <django.db.models.fields.DateTimeField: first_published_at>,
 <django.db.models.fields.DateTimeField: last_published_at>,
 <django.db.models.fields.DateTimeField: latest_revision_created_at>,
 <django.db.models.fields.related.ForeignKey: live_revision>,
 <django.db.models.fields.related.OneToOneField: translatable_page_ptr>,
 <django.db.models.fields.related.ForeignKey: canonical_page>,
 <django.db.models.fields.related.ForeignKey: language>,
 <django.db.models.fields.related.OneToOneField: translatablepage_ptr>,
 <django.db.models.fields.related.ForeignKey: main_image>,
 <django.db.models.fields.CharField: main_image_caption_override>,
 <django.db.models.fields.CharField: main_image_credit_override>,
 <django.db.models.fields.CharField: main_image_alttext_override>,
 <django.db.models.fields.related.ForeignKey: thumbnail_image>,
 <django.db.models.fields.CharField: thumbnail_text>,
 <django.db.models.fields.CharField: search_meta>,
 <django.db.models.fields.related.ForeignKey: fb_og_image>,
 <django.db.models.fields.CharField: fb_og_title>,
 <django.db.models.fields.CharField: fb_og_description>,
 <django.db.models.fields.related.ForeignKey: twitter_card_image>,
 <django.db.models.fields.CharField: twitter_card_title>,
 <django.db.models.fields.CharField: twitter_card_alt_text>,
 <django.db.models.fields.CharField: twitter_card_description>,
 <wagtail.core.fields.RichTextField: excerpt>,
 <django.db.models.fields.IntegerField: legacy_id>,
 <django.db.models.fields.DateTimeField: original_publish_date>,
 <django.db.models.fields.related.ForeignKey: sticky_donate_banner>,
 <django.db.models.fields.related.ForeignKey: section_link>,
 <wagtail.core.fields.StreamField: body>,
 <django.db.models.fields.DateField: date>,
 <modules.core.fields.FooterStreamField: footer>,
 <django.db.models.fields.BooleanField: comments_open>,
 <django.db.models.fields.CharField: soundcloud_url>,
 <django.db.models.fields.related.ForeignKey: podcast_audio_link>,
 <django.db.models.fields.related.ForeignKey: content_license>,
 <django.db.models.fields.related.ForeignKey: article_type>,
 <django.db.models.fields.CharField: share_header>,
 <django.db.models.fields.CharField: featured_video_url>,
 <django.db.models.fields.BooleanField: synthesized_article_audio>,
 <django.db.models.fields.related.ForeignKey: synthesized_article_audio_file>,
 <django.db.models.fields.BooleanField: publish_synthesized_article_audio>,
 <django.db.models.fields.BooleanField: exclude_taggable_blocks_from_page>,
 <django.db.models.fields.BooleanField: author_comment_highlights>,
 <django.db.models.fields.BooleanField: micropayments_sharing>)

Do you have any idea what I might have done wrong?

The migration (with a load of other page types snipped out) looks like this...

# Generated by Django 3.1.6 on 2021-04-13 16:13

from django.db import migrations, models
import django.db.models.deletion
import modules.taxonomy.mixins

def articlepage_ids(apps, schema_editor):
    YourPage = apps.get_model('core', 'ArticlePage')
    for page in YourPage.objects.all():
        # copy the page pointer from wagtailtrans to the new wagtail page
        page.page_ptr = page.translatable_page_ptr
        page.save()

class Migration(migrations.Migration):

    dependencies = [
        ('wagtailcore', '0059_apply_collection_ordering'),
        ('core', '0168_auto_20210111_1707'),
    ]

    operations = [
        migrations.AddField(
            model_name='articlepage',
            name='page_ptr',
            field=models.OneToOneField(
                auto_created=True,
                blank=True,
                null=True,
                on_delete=django.db.models.deletion.CASCADE,
                parent_link=False,
                to='wagtailcore.page'
            ),
            preserve_default=False,
        ),
        migrations.RunPython(articlepage_ids),
        migrations.AlterField(
            model_name='articlepage',
            name='page_ptr',
            field=models.OneToOneField(
                auto_created=True,
                on_delete=django.db.models.deletion.CASCADE,
                parent_link=True,
                to='wagtailcore.page'
            ),
            preserve_default=False,
        ),

[...snip]

EDIT: Actually, that fields output above was from before I added Page to the class definition, with Page added, I do see <django.db.models.fields.related.OneToOneField: page_ptr>, in there. Trying to look one up though gives this...

ProgrammingError: column core_articlepage.page_ptr_id does not exist
LINE 1: ...re_articlepage" INNER JOIN "wagtailcore_page" ON ("core_arti...

I'm going to edit the migration to remove that field before we add it back in. Will report back.

Adding a RemoveField operation before the add field gave this error...

  File "/usr/lib/python3.8/site-packages/django/db/migrations/migration.py", line 114, in apply
    operation.state_forwards(self.app_label, project_state)
  File "/usr/lib/python3.8/site-packages/django/db/migrations/operations/fields.py", line 158, in state_forwards
    old_field = model_state.fields.pop(self.name)
KeyError: 'page_ptr'

And skipping the AddField step altogether gives this error...

django.db.utils.ProgrammingError: column core_articlepage.page_ptr_id does not exist
LINE 1: ...re_articlepage" INNER JOIN "wagtailcore_page" ON ("core_arti...
                                                             ^
ceelian commented 3 years ago

@drcongo thanks for the feedback. From my side collecting the feedback for improving the migration guide blog post is fine here. If the wagtailtrans team doesn't think this issue isn't the proper place, I will try to come up with a solution.

To your question/issue. I already recognised while migrating multiple wagtail sites, that the explained migration procedure isn't 100% applicable to all different setups but the idea should work. That's why I tried to write the reasons behind every step so that you can validate it this step also applies to your specific wagtail setup.

It is hard to fully understand what the root cause for your migration issues is, as the DB might have changed with every migration variant you already tried to apply. But it looks a bit that something got a bit out of order on the "Modify the migration file"-step from the blogpost. I try to summarise the core intention of this step here:

After you added the Page model to your YourPage:

YourPage(TranslatablePage, Page):
...

and generated the migrations, then you need to modify the generated migration file to do 3 things

  1. AddField must add blank=True and null=True to allow the field to be blank because we will not have the page_ptr's from the beginning.
  2. RunPython must transfer the translatable_page_ptr to the page_ptr
  3. AlterField removed the blank=True and null=True properties as we have now all page_ptr set by the previous step.

Regarding this error:

ProgrammingError: column core_articlepage.page_ptr_id does not exist LINE 1: ...re_articlepage" INNER JOIN "wagtailcore_page" ON ("core_arti..

It shouldn't exist if your migration had been applied/successfully run on the db. As the AddField should already add the field and the RunPython should fill it with the ids. Eventually you could check your DB and have a look if the column page_ptr_id was properly added and the id's were transfered from the translatable_page_ptr to page_ptr.

Maybe you accidentally run the migration before doing the changes and now the migration will not run again because django has already saved that migration in its django_migrations table because migrations should only run once per DB.

Hope that helps or at least guides you in the right direction for solving your issue.

andreaslans commented 3 years ago

First of, thank you @ceelian for your hard work in documenting this process.

But, I was wondering. Is there any intent from wagtailtrans or wagtail to make this process smoother, by a command script or such. Or is this documentation what we are going to use if we want to migrate from wagtailtrans to wagtails native language support?

drcongo commented 3 years ago

Thanks for the extra detail @ceelian - I've just been through this from the start again, very carefully but I still get the same issue.

django.core.exceptions.FieldError: Auto-generated field 'page_ptr' in class 'ArticlePage' for parent_link to base class 'Page' clashes with declared field of the same name.

Checking the DB, there is no page_ptr_id column.

With both bases ArticlePage(TranslatablePage, Page) outputting ArticlePage._meta.local_fields shows the page_ptr field...

In [1]: ArticlePage._meta.local_fields
Out[1]:
[<django.db.models.fields.related.OneToOneField: page_ptr>,
 <django.db.models.fields.related.OneToOneField: translatablepage_ptr>,
[...]

If I take the Page base back out, ArticlePage._meta.local_fields the page_ptr field disappears...

In [1]: ArticlePage._meta.local_fields
Out[1]:
[<django.db.models.fields.related.OneToOneField: translatablepage_ptr>,
[...]

Like you say, it's hard to write instructions that work for every use case, and this is a pretty complex site that I'm trying to upgrade. I'm mainly leaving this extra detail here in case anyone passing through happens to have the same problem but also knows what that error message actually means!

Update: This appears to be the problem I'm running into https://stackoverflow.com/a/61723620 however I've read it twice and still haven't quite understood it. I'll report back if I ever do.

ceelian commented 3 years ago

@drcongo

django.core.exceptions.FieldError: Auto-generated field 'page_ptr' in class 'ArticlePage' for parent_link to base class 'Page' clashes with declared field of the same name.

Just guessing, but it might be that you forgot to remove the bases line in the migrations.

bases=('wagtailecore.page',),

If you don't remove the bases from the existing migrations of your page, then the migration system doesn't allow you to add an additional field called page_ptr to the new migrations because this clashes with the implicitly added fields when declaring bases=('wagtailecore.page',),

drcongo commented 3 years ago

Ooooooh, interesting. That part of the blog post comes after the python manage.py migrate where I get stuck, so I'd never actually read that part. Thanks for putting so much time into this @ceelian, you're a star.

bruecksen commented 2 years ago

@ceelian thanks for the great write up. I managed to move a quite big site to the new wagtail localize universe and I'm really happy to be on track again.

Only thing that wasn't quite clear to me from the beginning, is that if you follow the guide in a development environment and then want to do it on a production environment you can't just commit, push and deploy everything. You need to do it step by step as well on the production environment. I grouped the steps in separate commits and then rolled it out. That worked well.