Создание кастомной миграции
При создании новых моделей с переводом необходимо выполнить makemigrations
для создания миграции моделей, а затем выполнить миграцию для синхронизации изменений в базе данных. Однако при преобразовании существующих полей, вероятно, появляются данные в базе данных, которые необходимо сохранить. Мы собираемся перенести наши текущие данные в новые модели перевода. Поэтому мы добавили переведенные поля, но мы преднамеренно сохранили исходные поля.
Для добавления переводов в существующие поля выполните следующие действия:
- Мы создаем перенос новых полей модели для перевода, сохраняя исходные поля.
- Мы создаем пользовательский перенос для копирования данных из существующих полей в модели перевода.
- Мы удаляем существующие поля из исходных моделей.
Выполните следующую команду, чтобы создать миграцию для полей переводов, добавленных в модели Category и Product:
python manage.py makemigrations shop --name "add_translation_model"
Вы должны увидеть следующий вывод:
Migrations for 'shop':
0002_add_translation_model.py:
- Create model CategoryTranslation
- Create model ProductTranslation
- Change Meta options on category
- Alter index_together for product (0 constraint(s))
- Add field master to producttranslation
- Add field master to categorytranslation
- Alter unique_together for producttranslation (1 constraint(s))
- Alter unique_together for categorytranslation (1 constraint(s))
Перенос существующих данных
Теперь необходимо создать кастомную миграцию для копирования существующих данных в новые модели перевода. Создайте пустую миграцию с помощью этой команды:
python manage.py makemigrations --empty shop --name "migrate_translatable_fields"
Вы получите следующий результат:
Migrations for 'shop':
0003_migrate_translatable_fields.py
Отредактируйте файл shop/migrations/0003_migrate_translatable_fields.py и добавьте в него следующий код:
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
from django.apps import apps
from django.conf import settings
from django.core.exceptions import ObjectDoesNotExist
translatable_models = {
'Category': ['name', 'slug'],
'Product': ['name', 'slug', 'description'],
}
def forwards_func(apps, schema_editor):
for model, fields in translatable_models.items():
Model = apps.get_model('shop', model)
ModelTranslation = apps.get_model('shop', '{}Translation'.format(model))
for obj in Model.objects.all():
translation_fields = {field: getattr(obj, field) for field in fields}
translation = ModelTranslation.objects.create(
master_id=obj.pk,
language_code=settings.LANGUAGE_CODE,
**translation_fields)
def backwards_func(apps, schema_editor):
for model, fields in translatable_models.items():
Model = apps.get_model('shop', model)
ModelTranslation = apps.get_model('shop', '{}Translation'.format(model))
for obj in Model.objects.all():
translation = _get_translation(obj, ModelTranslation)
for field in fields:
setattr(obj, field, getattr(translation, field))
obj.save()
def _get_translation(obj, MyModelTranslation):
translations = MyModelTranslation.objects.filter(master_id=obj.pk)
try:
# Try default translation
return translations.get(language_code=settings.LANGUAGE_CODE)
except ObjectDoesNotExist:
return translations.get()
class Migration(migrations.Migration):
dependencies = [
('shop', '0002_add_translation_model'),
]
operations = [
migrations.RunPython(forwards_func, backwards_func),
]
Эта миграция включает функции forwards_func() и backwards_func(), содержащие код, который должен быть выполнен для применения/изменения миграции.
- Процесс миграции работает следующим образом:
- Мы определим модели и их переводимые поля в словаре translatable_models.
- Чтобы применить миграцию, мы перейдем к моделям, включающим переводы для получения модели и ее преобразованных классов модели с app.get_model().
- Мы перейдем все существующие объекты в базе данных и создадим объект перевода для LANGUAGE_CODE, определенного в параметрах проекта. Мы включаем ForeignKey в исходный объект и копию для каждого поля преобразования из исходных полей.
Функция backward извлекает объект перевода по умолчанию и копирует значения переведенных полей обратно в исходный объект.
Мы создали миграцию для добавления полей перевода, а затем переноса для копирования содержимого из существующих полей в новые модели перевода.
Наконец, нам нужно удалить исходные поля, которые нам больше не нужны. Отредактируйте файл models.py приложения shop и удалите поля name и slug модели Category. Поля модели Category теперь должны выглядеть следующим образом:
class Category(TranslatableModel):
translations = TranslatedFields(
name=models.CharField(max_length=200, db_index=True),
slug=models.SlugField(max_length=200, db_index=True, unique=True)
)
Удалите поля name, slug, и description модели Product. Теперь она должна выглядеть следующим образом:
class Product(TranslatableModel):
translations = TranslatedFields(
name=models.CharField(max_length=200, db_index=True),
slug=models.SlugField(max_length=200, db_index=True),
description=models.TextField(blank=True)
)
category = models.ForeignKey(Category, related_name='products')
image = models.ImageField(upload_to='products/%Y/%m/%d', blank=True)
price = models.DecimalField(max_digits=10, decimal_places=2)
stock = models.PositiveIntegerField()
available = models.BooleanField(default=True)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
Теперь необходимо создать окончательную миграцию, которая отражала бы это изменение модели. Однако, если мы попытаемся запустить утилиту manage.py, мы получим ошибку, потому что мы еще не адаптировали админ-панель к моделям, которые можно перевести.