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

Я искал ответ на это на сайте 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'), но я не уверен, как исправить внешний ключ в этом случае.
  • Более простой способ, которым вы знаете?

Ответ 1

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

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

Я ответил "нет", и все было в порядке.

Ответ 2

Внесите изменения в 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)

Ответ 3

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

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

Ответ 4

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

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