Самый простой способ переименовать модель, используя Django / South?

138

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

Я хочу переименовать модель Django, используя Юг. Скажем, у вас есть следующее:

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.

Какой самый простой способ сделать это с помощью Юга?

  • Я мог бы, возможно, выполнить перенос данных, но это кажется довольно привлекательным.
  • Напишите пользовательскую миграцию, например. db.rename_table('city_citystate', 'geo_citystate'), но я не уверен, как исправить внешний ключ в этом случае.
  • Более простой способ, которым вы знаете?
Теги:
model
rename
django-south

4 ответа

122
Лучший ответ

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

./manage.py schemamigration yourapp rename_foo_to_bar --empty

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

Если вы используете более старую версию юга, вам понадобится 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 (). Кажется, это сработало очень хорошо.
  • 0
    Когда я делаю это, я теряю все строки в foo. Что я делаю неправильно?
Показать ещё 11 комментариев
69

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

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

db.rename_column(myapp_model, foo_id, bar_id)
  • 8
    +1 за переименование модели в таблице contenttypes
  • 2
    Я получаю сообщение об ошибке KeyError: «Модель« contenttype »из приложения« contenttypes »недоступна в этой миграции». Кроме того, у меня есть таблица django_content_type, но нет таблицы типов содержимого. (Джанго 1.6)
Показать ещё 2 комментария
5

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

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

  • 7
    Или переименуйте модель в коде, но используйте мета-опцию db_table чтобы имя таблицы базы данных db_table прежним.
  • 0
    @Daniel - знаете ли вы, что db_table используется для получения имен внешних ключей?
Показать ещё 2 комментария
-2

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

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

Ещё вопросы

Сообщество Overcoder
Наверх
Меню