Making existing fields translatable¶
The following guide explains how to make existing fields translatable, and migrate the data from the old fields to translated fields.
django-parler stores translated fields in a separate model, so it can store multiple versions (translations) of the same field. To make existing fields translatable, 3 migration steps are needed:
Create the translation table, keep the existing columns
Copy the data from the original table to the translation table.
Remove the fields from the original model.
The following sections explain this in detail:
Step 1: Create the translation table¶
Say we have the following model:
class MyModel(models.Model):
name = models.CharField(max_length=123)
First create the translatable fields:
class MyModel(TranslatableModel):
name = models.CharField(max_length=123)
translations = TranslatedFields(
name=models.CharField(max_length=123),
)
Now create the migration:
manage.py makemigrations myapp --name "add_translation_model"
Step 2: Copy the data¶
Within the data migration, copy the existing data:
Create an empty migration:
manage.py makemigrations --empty myapp --name "migrate_translatable_fields"
And use it to move the data:
from django.db import migrations
from django.conf import settings
from django.core.exceptions import ObjectDoesNotExist
def forwards_func(apps, schema_editor):
MyModel = apps.get_model('myapp', 'MyModel')
MyModelTranslation = apps.get_model('myapp', 'MyModelTranslation')
for object in MyModel.objects.all():
MyModelTranslation.objects.create(
master_id=object.pk,
language_code=settings.LANGUAGE_CODE,
name=object.name
)
def backwards_func(apps, schema_editor):
MyModel = apps.get_model('myapp', 'MyModel')
MyModelTranslation = apps.get_model('myapp', 'MyModelTranslation')
for object in MyModel.objects.all():
translation = _get_translation(object, MyModelTranslation)
object.name = translation.name
object.save() # Note this only calls Model.save()
def _get_translation(object, MyModelTranslation):
translations = MyModelTranslation.objects.filter(master_id=object.pk)
try:
# Try default translation
return translations.get(language_code=settings.LANGUAGE_CODE)
except ObjectDoesNotExist:
try:
# Try default language
return translations.get(language_code=settings.PARLER_DEFAULT_LANGUAGE_CODE)
except ObjectDoesNotExist:
# Maybe the object was translated only in a specific language?
# Hope there is a single translation
return translations.get()
class Migration(migrations.Migration):
dependencies = [
('yourappname', '0001_initial'),
]
operations = [
migrations.RunPython(forwards_func, backwards_func),
]
Note
Be careful which language is used to migrate the existing data.
In this example, the backwards_func()
logic is extremely defensive not to loose translated data.
Step 3: Remove the old fields¶
Remove the old field from the original model. The example model now looks like:
class MyModel(TranslatableModel):
translations = TranslatedFields(
name=models.CharField(max_length=123),
)
Create the database migration, it will simply remove the original field:
manage.py makemigrations myapp --name "remove_untranslated_fields"
Updating code¶
The project code should be updated. For example:
Replace
filter(field_name)
with.translated(field_name)
orfilter(translations__field_name)
.Make sure there is one filter on the translated fields, see Using multiple filter() calls.
Update the
ordering
andorder_by()
code. See The ordering meta field.Update the admin
search_fields
andprepopulated_fields
. See Using search_fields in the admin.
Deployment¶
To have a smooth deployment, it’s recommended to only run the first 2 migrations - which create columns and move the data. Removing the old fields should be done after reloading the WSGI instance.