Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrating untranslatable fields to hvad-translated fields #330

Open
spectras opened this issue Feb 16, 2017 · 3 comments
Open

Migrating untranslatable fields to hvad-translated fields #330

spectras opened this issue Feb 16, 2017 · 3 comments
Assignees

Comments

@spectras
Copy link
Collaborator

Hello!
It was discussed in another issue, but I cannot find it. So I'll try to summarize it from memory. Let's say you have this model:

class MyModel(models.Model):
    foo_field = models.CharField()    # this one will not be translatable
    bar_field = models.CharField()    # this one will be translatable

The best way to handle an initial migration to hvad is like this:

  • In the to-be-made-translatable models, rename all translatable fields:

    class MyModel(models.Model):
        foo_field = models.CharField()
        bar_field_temp = models.CharField()

    Create a Django migration for this step. The automatic migration will not work (it will delete the field and recreate it, loosing all data). You must create an empty migration like this:

    python manage.py makemigrations --empty myApp

    Then, in the generated migration's operations, add the following line for each field to rename:

    operations = [
        migrations.RenameField('MyModel', 'bar_field', 'bar_field_temp'),
    ]

    Apply the migration, and move onto the next step.

  • Then, create the translatable fields themselves, alongside the renamed fields. Don't forget to make the model inherit from TranslatableModel now:

    class MyModel(TranslatableModel):
        foo_field = models.CharField()
        bar_field_temp = models.CharField()
        translations = TranslatedFields(
            bar_field = models.CharField(),
        )

    Create another migration. Django will build it correctly, so let it do its job:

    python manage.py makemigrations myApp

    Apply the migration, and move onto the next step.

  • Now, we must copy over the data from the temporary fields into the new fields. You must decide the language they will be assigned to. Let's say English. Create yet another migration, empty again. Write a small function at the top, to copy over data. Note that hvad features are not available in migrations, so we must explicitly address the translations' model. For instance:

    def copy_mymodel_fields_to_translations(apps, schema_editor):
        MyModel = apps.get_model('myapp', 'MyModel')
        MyModelTranslation = apps.get_model('myapp', 'MyModelTranslation')
        for instance in MyModel.objects.all():
            MyModelTranslation.objects.create(
                master=instance,
                language_code='en',
                bar_field=instance.bar_field_temp,
            )

    It is usually a good practice to make migrations reversible. For this, we will need another function that will copy back English fields to the temporary fields:

    def copy_mymodel_fields_from_translations(apps, schema_editor):
        MyModel = apps.get_model('myapp', 'MyModel')
        MyModelTranslation = apps.get_model('myapp', 'MyModelTranslation')
        for obj in MyModelTranslation.objects.filter(language_code='en').select_related('master'):
            obj.master.bar_field_temp = obj.bar_field
            obj.master.save()

    Now, just glue together the whole thing by adding those functions as a migration step:

    operations = [
        migrations.RunPython(copy_mymodel_fields_to_translations,
                                 reverse_code=copy_mymodel_fields_from_translations)
    ]

    Apply the migration, and move onto the next step.

  • We're almost done. All we need now is to remove the temporary field. Simply do that:

    class MyModel(TranslatableModel):
        foo_field = models.CharField()
        translations = TranslatedFields(
            bar_field = models.CharField(),
        )

    Create the last migration. Django will build it correctly too, so let it do its job:

    python manage.py makemigrations myApp

    Apply the migration, and you're done!

That should do it. Let me know if you run into any issue, and as always try running the migrations on you dev environment first, both forwards and backwards.

Also, remember that even though the migration will be fully reversible, applying it backward will delete all translations that are not in the language you chose as a default language.

@daesu
Copy link

daesu commented Feb 21, 2017

Thanks very much. Works well

@astagi
Copy link

astagi commented Oct 17, 2018

Hey @spectras we made a simple tool to achieve this

https://github.com/lotrekagency/hvad-migration

It's not ready for production yet, it needs more tests, but it works for our purpose at the moment. Let me know what you think about

@ThibaultLemaire
Copy link

Yep, definitely needs doc. Migrating from untranslated models is a common use case, and yet there is no mention of that in the doc.

I had to assume there was no particular procedure and that I could translate afterwards, hit the wall, curse hvad for not working the way I expected, to eventually find this issue. I had a hunch something like that would happen, but since the doc says nothing about migrating untranslated model...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants