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

Я искал ответ на этот вопрос на сайте Юга, в 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. Проще ли это узнать?

Ответы [ 4 ]

129 голосов
/ 24 мая 2010

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

./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'.

Я ответил "нет", и все, казалось, было хорошо.

66 голосов
/ 11 сентября 2012

Внесите изменения в 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)
5 голосов
/ 19 мая 2010

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

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

0 голосов
/ 19 декабря 2014

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

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

...