Найпростіший спосіб перейменувати модель за допомогою Django / South?


141

Я шукав відповідь на це на веб-сайті South, 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 за підказку.)

Якщо ви використовуєте старішу версію на південь, вам знадобиться 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
Мені вдалося уникнути повідомлення про помилку, яке Леопд створив міграцію схеми, використовуючи --auto замість --пусто. Потім я відредагував файл міграції, змінивши видалення / створення таблиць у виклик db.rename_table (). Здається, це спрацювало дуже добре.
KFB

4
Я використав цю техніку 2.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, але не таблиця типів контенту. (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 Я думаю, що параметр freeze не заморожений. south.readthedocs.io/en/latest/ormfreezing.html Але дуже дякую за вашу допомогу, це було дуже корисно.
ccsakuweb

5

Південь не може зробити це сам - як він знає, що Barявляє собою те, що Fooраніше? Це такий варіант, для якого я б написав спеціальну міграцію. Ви можете змінити свій ForeignKeyкод, як це було зроблено вище, і тоді це лише випадок перейменування відповідних полів і таблиць, які ви можете робити будь-яким способом.

Нарешті, чи справді вам потрібно це зробити? Мені ще не потрібно перейменовувати моделі - назви моделей - лише деталь реалізації - особливо враховуючи наявність verbose_nameопції Meta.


7
Крім того, перейменуйте модель в код, але використовуйте параметр db_tableMeta, щоб зберегти ім'я таблиці бази даних однаковим.
Даніель Роузман

@Daniel - чи знаєте ви, чи db_tableвикористовується для отримання імен іноземних ключів?
Домінік Роджер

я вірю, що це так. Якщо ви зміните ім'я моделі та встановите db_table, все має працювати як слід.
Давор Лучич

1
@DanielRoseman - це найкраще рішення в цілій нитці!
joerick

-1

Я слідував за рішенням Леопда вище. Але це не змінило назв моделей. Я змінив його вручну в коді (також у споріднених моделях, де це називається FK). І зробили ще одну міграцію на південь, але з варіантом --fake. Це робить імена моделей та назви таблиць однаковими.

Щойно зрозумів, можна спочатку почати зі зміни назв моделі, а потім редагувати файл міграцій перед їх застосуванням. Значно чистіше.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.