Использование 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.

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

Обновление:

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

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


person Spike    schedule 18.08.2011    source источник
comment
Вы неправильно используете абстрактный базовый класс. Вам нужно поставить class Meta: abstract = True на свой BaseModel, а не на его потомков. docs.djangoproject.com/en/1.3/topics /дб/модели/   -  person Etienne    schedule 18.08.2011
comment
ой, конечно... я внес изменения, чтобы отразить это   -  person Spike    schedule 18.08.2011
comment
Ссылка битая :/   -  person Medeiros    schedule 12.12.2013
comment
@Medeiros извините за это! Я только что обновил его, так что он должен снова работать нормально.   -  person Spike    schedule 16.12.2013


Ответы (2)


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

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

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

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

person Seb    schedule 20.08.2011
comment
Спасибо, что указали, как Юг удалит BaseModel. Вы меня очень порадовали, что я задал этот вопрос! Я сделаю это в течение следующей недели или двух и отчитаюсь о том, как все идет. - person Spike; 21.08.2011
comment
Я отмечаю это как принятое, так как вы направили меня на правильный путь. При этом есть несколько подводных камней, но мне удалось заставить все работать после долгой ночи прошлой ночью. В итоге осталось еще несколько шагов. Подробнее об этом я писал здесь: markliu.me/2011/aug/23/ - person Spike; 24.08.2011
comment
Только что понял, что моя ссылка битая. Вот новый: markliu.me/2011/aug/23/ - person Spike; 26.02.2013

Ответ Себастьяна Трепчи, вероятно, хорош, но другим способом сделать это будет создание миграции вручную:

  1. Добавьте abstract = True к вашей базовой модели.

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

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

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

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

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

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

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

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

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

Дополнительную информацию можно найти в South API.

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

person Etienne    schedule 21.08.2011
comment
Интересно, спасибо, что дали мне второй вариант. Метод Себастьяна кажется мне менее грязным, поэтому я собираюсь сначала попробовать его. Я предпочитаю иметь возможность разделять миграцию схемы и данных. - person Spike; 21.08.2011
comment
Это одно из преимуществ моего метода: вы не переносите никаких данных, вы просто переносите схему. Но я согласен с вами, что мой метод сложнее. - person Etienne; 21.08.2011
comment
Должен добавить, что я ошибался. Мой метод предполагал (ошибочно), что в вашем BaseModel нет хороших данных (т.е. нет данных, которые необходимо перенести в дочерние модели). Вероятно, это не так. Итак, если вам нужно перенести данные из BaseModel, между a. и б. вам нужно добавить недостающие столбцы в дочерние модели (db.add_column(table_name, field_name, field)), а затем скопировать данные (for row in orm.BaseModel.all():). - person Etienne; 23.08.2011
comment
+1 за указание, как мне пришлось переименовать поле primary_key в моих дочерних моделях. Оказывается, я сделал, и это было полезно искать. - person Spike; 24.08.2011