Использование Django South для перехода от конкретного наследования к абстрактному наследованию

У меня есть существующий проект Django, который имеет несколько моделей, использующих конкретное наследование базового класса. После более пристального рассмотрения и после прочтения о том, что люди, подобные Якобу Каплану-Мосу должны сказать об этом, использование этого конкретного наследования в моем случае не нужно. Я хотел бы перейти на использование абстрактного базового класса.

Дело в том, что этот сложный процесс заключается в том, что мой сайт активен, и я ввел данные пользователя. Таким образом, мне нужно сохранить все мои данные в течение всего этого перехода.

Я приведу пример, чтобы быть более конкретным:

До:

app1/models.py:

class Model1(base_app.models.BaseModel):
    field1 = models.CharField(max_length=1000)
    field2 = models.CharField(max_length=1000)

app2/models.py:

class Model2(base_app.models.BaseModel):
    field1 = models.CharField(max_length=1000)
    field2 = models.CharField(max_length=1000)

base_app/models.py:

class BaseModel(models.Model):
    user = models.ForeignKey(User)
    another_field = models.CharField(max_length=1000)

После

app1/models.py:

class Model1(base_app.models.BaseModel):
    field1 = models.CharField(max_length=1000)
    field2 = models.CharField(max_length=1000)

app2/models.py:

class Model2(base_app.models.BaseModel):
    field1 = models.CharField(max_length=1000)
    field2 = models.CharField(max_length=1000)

base_app/models.py:

class BaseModel(models.Model):
    user = models.ForeignKey(User)
    another_field = models.CharField(max_length=1000)

    class Meta:
        abstract = True

Сейчас я планирую сначала добавить abstract = True в BaseModel. Затем для каждой модели, которая использует BaseModel, по одному за раз:

  • Используйте юг для переноса базы данных и создания этой миграции с помощью флага --auto
  • Используйте южную миграцию данных. Например, я прокрутил бы каждый объект в Model1 для извлечения объекта в BaseModel, который имеет тот же pk и скопирует значения для каждого поля объекта BaseModel для объекта Model1.

Итак, во-первых, будет ли это работать? И, во-вторых, есть ли лучший способ сделать это?

Update:

Мое окончательное решение подробно описано здесь:

http://www.markliu.me/2011/aug/23/migrating-a-django-postgres-db-from-concrete-inhe/

Ответ 1

  • Добавьте NewBaseModel, мы используем другое имя, чтобы оно не противоречило текущему не абстрактному (в противном случае Юг фактически удалил бы BaseModel).

    class NewBaseModel(models.Model):
        user = models.ForeignKey(User)
        another_field = models.CharField(max_length=1000)
    
        class Meta:
            abstract = True
    
  • Задайте Model1 и Model2 для наследования из NewBaseModel

  • Запустить схематизацию --авто, добавлены 2 новых поля в Model1 и Model2
  • Запустить datamigration -empty и заполнить новые поля из значений в BaseModel
  • Загрузите db и дважды проверьте, все ли выполнено правильно.
  • Удалить BaseModel и переименовать NewBaseModel в BaseModel
  • Запустить схематизацию --auto (это должно работать;))
  • Развертывание!

ПРИМЕЧАНИЕ. При переносе используйте переменную orm, чтобы использовать текущее состояние вашей модели.

Ответ 2

Ответ Sebastjan Trepča, вероятно, хорош, но другой способ сделать это - создать миграцию вручную:

  • Добавьте abstract = True в базовую модель.

  • Запустите schemamigration --auto, сгенерированная миграция, вероятно, не будет хорошей, но вы будете использовать ее в качестве базы.

  • Отредактируйте файл миграции. В forward вы должны добавить в следующем порядке:

    а. db.delete_foreign_key(table_name, column) для каждой из моделей ваших детей. Это приведет к удалению ForeignKey между родительской и дочерней таблицами.

    б. db.delete_table(BaseModel), чтобы удалить таблицу базовой модели (эта команда должна быть, вероятно, уже создана, сгенерирована --auto).

    с. Возможно, вам придется переименовать весь столбец первичного ключа ваших моделей детей в "id". Я не уверен в этом. Если вам нужно сделать это: db.rename_column(table_name, column_name, 'id') для каждой из моделей ваших детей.

  • Удалите весь автоматически сгенерированный код в forward, который не имеет смысла.

  • Запустите миграцию: migrate

Что делает этот метод, это удаление таблицы базового класса и внешних ключей между таблицей базового класса и ее дочерними элементами, поскольку они не используются с абстрактным базовым классом.

Я не тестировал этот метод, поэтому вы можете столкнуться с некоторыми проблемами. Этот подход более сложный, чем другой, но преимущества в том, что вам не нужно переносить данные и что вы поймете, что происходит. Он должен работать довольно быстро, это хорошо для живой миграции.

Для получения дополнительной информации вы можете обратиться к South API.

Одна действительно важная вещь в любом методе, который вы будете использовать, работать с локальной копией вашей системы и базы данных. Когда вы действительно убедитесь, что миграция работает хорошо, создайте резервную копию своей производственной базы данных, затем примените перенос, а затем перезапустите веб-сервер (чтобы загрузить измененный код модели).