Создание кастомной миграции

При создании новых моделей с переводом необходимо выполнить makemigrations для создания миграции моделей, а затем выполнить миграцию для синхронизации изменений в базе данных. Однако при преобразовании существующих полей, вероятно, появляются данные в базе данных, которые необходимо сохранить. Мы собираемся перенести наши текущие данные в новые модели перевода. Поэтому мы добавили переведенные поля, но мы преднамеренно сохранили исходные поля.

Для добавления переводов в существующие поля выполните следующие действия:

  1. Мы создаем перенос новых полей модели для перевода, сохраняя исходные поля.
  2. Мы создаем пользовательский перенос для копирования данных из существующих полей в модели перевода.
  3. Мы удаляем существующие поля из исходных моделей.

Выполните следующую команду, чтобы создать миграцию для полей переводов, добавленных в модели 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(), содержащие код, который должен быть выполнен для применения/изменения миграции.

  1. Процесс миграции работает следующим образом:
  2. Мы определим модели и их переводимые поля в словаре translatable_models.
  3. Чтобы применить миграцию, мы перейдем к моделям, включающим переводы для получения модели и ее преобразованных классов модели с app.get_model().
  4. Мы перейдем все существующие объекты в базе данных и создадим объект перевода для 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, мы получим ошибку, потому что мы еще не адаптировали админ-панель к моделям, которые можно перевести.

results matching ""

    No results matching ""