Самый простой способ переименовать модель с помощью Django / South?

141

Я искал ответ на этот вопрос на сайте Юга, в Google и SO, но не мог найти простого способа сделать это.

Я хочу переименовать модель Django с помощью South. Допустим, у вас есть следующее:

class Foo(models.Model):
    name = models.CharField()

class FooTwo(models.Model):
    name = models.CharField()
    foo = models.ForeignKey(Foo)

и вы хотите преобразовать Foo в Bar, а именно

class Bar(models.Model):
    name = models.CharField()

class FooTwo(models.Model):
    name = models.CharField()
    foo = models.ForeignKey(Bar)

Чтобы не усложнять, я просто пытаюсь изменить имя с Fooна Bar, но пока игнорирую fooчлена FooTwo.

Как проще всего это сделать, используя Юг?

  1. Возможно, я мог бы выполнить миграцию данных, но это кажется довольно сложным.
  2. Напишите, например db.rename_table('city_citystate', 'geo_citystate'), настраиваемую миграцию, но я не уверен, как исправить внешний ключ в этом случае.
  3. Более простой способ, который вы знаете?
вонкоч
источник
5
См. Также stackoverflow.com/questions/3235995/… для переименования поля модели, а не модели .
Механическая улитка
Оптимизированное решение для Django> = 1.8 stackoverflow.com/questions/25091130/…
Химический программист

Ответы:

130

Чтобы ответить на ваш первый вопрос, простое переименование модели / таблицы довольно просто. Выполните команду:

./manage.py schemamigration yourapp rename_foo_to_bar --empty

(Обновление 2: попробуйте --autoвместо того, --emptyчтобы избежать предупреждения ниже. Спасибо @KFB за подсказку.)

Если вы используете старую версию south, вам понадобится startmigrationвместо schemamigration.

Затем вручную отредактируйте файл миграции, чтобы он выглядел так:

class Migration(SchemaMigration):

    def forwards(self, orm):
        db.rename_table('yourapp_foo', 'yourapp_bar')


    def backwards(self, orm):
        db.rename_table('yourapp_bar','yourapp_foo')   

Вы можете сделать это проще, используя db_tableопцию Meta в вашем классе модели. Но каждый раз, когда вы это делаете, вы увеличиваете унаследованный вес своей кодовой базы - если имена классов отличаются от имен таблиц, ваш код сложнее понимать и поддерживать. Для ясности я полностью поддерживаю такие простые рефакторинги.

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

The following content types are stale and need to be deleted:

    yourapp | foo

Any objects related to these content types by a foreign key will also
be deleted. Are you sure you want to delete these content types?
If you're unsure, answer 'no'.

Я ответил «нет», и вроде бы все нормально.

Леопд
источник
3
Мне удалось избежать сообщения об ошибке Leopd, создав миграцию схемы с использованием --auto вместо --empty. Затем я отредактировал файл миграции, изменив удаление / создание таблиц на вызов db.rename_table (). Похоже, это сработало очень хорошо.
KFB
4
Я использовал эту технику 02.09.2011, без ошибок. Возможно более новая версия South решила проблему с ошибками.
Чип Тол
1
Спасибо, что держите это в курсе! В приведенном ниже ответе Цзянь говорится, что важно сохранять вызовы send_create_signal, знаете ли вы об этом? Если вы согласны, было бы здорово обновить ваш пример миграции.
mrooney
5
Помните, что это не приведет к переименованию индексов в этой таблице. Если вы создадите новую таблицу в будущем с тем же именем, что и старая таблица, вы можете получить ошибки из-за коллизии имен индексов. Мы использовали эту технику, но с этого момента мы собираемся явно создать новую таблицу, перенести данные, а затем удалить старую таблицу.
Джереми
3
Имена столбцов в автоматически созданных таблицах, таких как таблицы M2M в исходной модели, также не переносятся этим методом.
spookylukey
66

Внесите изменения models.pyи запустите

./manage.py schemamigration --auto myapp

Когда вы проверите файл миграции, вы увидите, что он удаляет таблицу и создает новую.

class Migration(SchemaMigration):

    def forwards(self, orm):
        # Deleting model 'Foo'                                                                                                                      
        db.delete_table('myapp_foo')

        # Adding model 'Bar'                                                                                                                        
        db.create_table('myapp_bar', (
        ...
        ))
        db.send_create_signal('myapp', ['Bar'])

    def backwards(self, orm):
        ...

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

class Migration(SchemaMigration):

    def forwards(self, orm):
        # Renaming model from 'Foo' to 'Bar'                                                                                                                      
        db.rename_table('myapp_foo', 'myapp_bar')                                                                                                                        
        if not db.dry_run:
            orm['contenttypes.contenttype'].objects.filter(
                app_label='myapp', model='foo').update(model='bar')

    def backwards(self, orm):
        # Renaming model from 'Bar' to 'Foo'                                                                                                                      
        db.rename_table('myapp_bar', 'myapp_foo')                                                                                                                        
        if not db.dry_run:
            orm['contenttypes.contenttype'].objects.filter(app_label='myapp', model='bar').update(model='foo')

В случае отсутствия updateоператора db.send_create_signalвызов создаст новый ContentTypeс новым названием модели. Но лучше использовать updateтот, ContentTypeкоторый у вас уже есть, если на него указывают объекты базы данных (например, через a GenericForeignKey).

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

db.rename_column(myapp_model, foo_id, bar_id)
Цзянь
источник
2
Я получаю сообщение об ошибке KeyError: «Модель contenttype из приложения contenttypes недоступна в этой миграции». Кроме того, у меня есть таблица django_content_type, но не таблица contenttypes. (Django 1.6)
Сет
2
@Seth Я работал над этим, выполняя обновление моделей ContentType в отдельной миграции данных и добавляя contenttypes.ContentTypeмодель к замороженным моделям с помощью --frozenфлага ./manage.py datamigration. Например: ./manage.py datamigration --frozen contenttypes myapp update_contenttypes. Затем отредактируйте myapp_migrations / NNNN_update_contenttypes.py, указав код обновления типа контента, как указано выше.
Джеффри Хинг
@GeoffreyHing Я думаю, что параметр не замораживается. south.readthedocs.io/en/latest/ormfreezing.html Но большое спасибо за вашу помощь, это было действительно полезно.
ccsakuweb 09
5

Юг не может этого сделать сам - откуда он знает, что Barпредставляет то, что Fooраньше? Я бы написал для этого специальную миграцию. Вы можете изменить свой ForeignKeyкод, как вы это делали выше, а затем просто переименовать соответствующие поля и таблицы, что вы можете делать как хотите.

Наконец, действительно ли вам нужно это делать? Мне еще не нужно было переименовывать модели - названия моделей - это всего лишь деталь реализации, особенно с учетом наличия verbose_nameопции Meta.

Доминик Роджер
источник
7
В качестве альтернативы можно переименовать модель в коде, но использовать db_tableопцию Meta, чтобы имя таблицы базы данных оставалось прежним.
Дэниел Роузман
@Daniel - знаете ли db_tableвы, используется ли он для получения имен внешних ключей?
Доминик Роджер
я верю, что это так. Если вы измените название модели и установите db_table, все должно работать должным образом.
Давор Лучич
1
@DanielRoseman это лучшее решение во всей ветке!
Joerick
-1

Я следил за решением Леопда выше. Но это не повлияло на названия моделей. Я вручную изменил его в коде (также в связанных моделях, где это называется FK). И сделал еще одну миграцию на юг, но с параметром --fake. Это делает имена моделей и имена таблиц одинаковыми.

Только что осознал, что можно сначала начать с изменения названий моделей, а затем отредактировать файл миграции перед их применением. Намного чище.

Gowthaman
источник