миграция изменения имени поля django-модели без потери данных - PullRequest
30 голосов
/ 17 августа 2010

У меня есть проект django с таблицей базы данных, которая уже содержит данные. Я хотел бы изменить имя поля без потери каких-либо данных в этом столбце. Мой первоначальный план состоял в том, чтобы просто изменить имя поля модели таким образом, чтобы фактически не изменить имя таблицы БД (используя параметр столбца db_column):

Оригинальная модель:

class Foo(models.Model):
  orig_name = models.CharField(max_length=50)

Новая модель:

class Foo(models.Model):
  name = models.CharField(max_length=50, db_column='orig_name')

Но при запуске schemamigration --auto Юга создается скрипт миграции, который удаляет исходный столбец orig_name и добавляет новый столбец name, что может привести к нежелательному побочному эффекту удаления данных в этом столбце. (Я также не понимаю, почему Саут хочет изменить имя столбца в БД, так как я понял, что db_column позволяет изменять имя поля модели без изменения имени столбца таблицы базы данных).

Если мне не удастся изменить поле модели без изменения поля db, думаю, я мог бы сделать более простое изменение имени, например:

Оригинальная модель:

class Foo(models.Model):
  orig_name = models.CharField(max_length=50)

Новая модель:

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

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

Требуется ли многоэтапный процесс? (например, 1. добавление столбца, 2. перенос данных из старого столбца в новый столбец и 3. удаление исходного столбца) Или я могу изменить скрипт миграции что-то вроде db.alter_column?

Каков наилучший способ сохранить данные в этом столбце при изменении имени столбца?

Ответы [ 7 ]

28 голосов
/ 18 октября 2015

Изменение имени поля при сохранении поля БД

Добавление ответа для Django 1.8+ (с миграциями из Django, а не на юг).

Выполните миграцию, которая сначала добавляет свойство db_column, а затем переименовывает поле. Джанго понимает, что первый - это неоперация (потому что он меняет db_column, чтобы остаться прежним), и что второй - это неоперация (потому что он не делает никаких изменений схемы). Я на самом деле проверил журнал, чтобы увидеть, что не было никаких изменений схемы ...

operations = [
    migrations.AlterField(
        model_name='mymodel',
        name='oldname',
        field=models.BooleanField(default=False, db_column=b'oldname'),
    ),
    migrations.RenameField(
        model_name='mymodel',
        old_name='oldname',
        new_name='newname',
    ),
]
17 голосов
/ 17 августа 2010

Это довольно легко исправить. Но вам придется изменить миграцию самостоятельно.

Вместо удаления и добавления столбца используйте db.rename_column. Вы можете просто изменить миграцию, созданную schemamigration --auto

5 голосов
/ 06 сентября 2016

На самом деле с Django 1.10, просто переименовав поле в модели и затем запустив makemigrations, сразу идентифицирует операцию (т.е. одно поле исчезло, другое появилось вместо него):

$ ./manage.py makemigrations
Did you rename articlerequest.update_at to articlerequest.updated_at (a DateTimeField)? [y/N] y
Migrations for 'article_requests':
  article_requests/migrations/0003_auto_20160906_1623.py:
    - Rename field update_at on articlerequest to updated_at
4 голосов
/ 29 января 2015

Я столкнулся с этой ситуацией.Я хотел изменить имена полей в модели, но оставить имена столбцов такими же.

То, как я это сделал, это сделать schemamigration --empty [app] [some good name for the migration].Проблема заключается в том, что для юга изменение имен полей в модели - это изменение , которое необходимо обработать.Таким образом, миграция должна быть создана.Однако мы знаем, что ничего не нужно делать на стороне базы данных. Таким образом, пустая миграция позволяет избежать ненужных операций с базой данных и в то же время удовлетворяет потребность Юга обрабатывать то, что она считает изменением.

Обратите внимание, что если вы используете loaddata или используете тест Джангоприспособление приспособления (которое использует loaddata за кулисами).Вам придется обновить осветительные приборы, чтобы использовать новое имя поля, поскольку осветительные приборы основаны на именах полей модели, а не на именах полей базы данных.

Для случаев, когда имена столбцов в базе данных меняются, я никогдарекомендуем использовать db.rename_column для миграции столбцов.Я использую метод, описанный sjh в этот ответ :

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

Как я уже отмечал в комментарии по этому вопросу, проблема с db.rename_column заключается в том, что он не переименовывает ограничения вместе сколонка.Является ли проблема просто косметической или это означает, что будущая миграция может потерпеть неудачу, потому что она не может найти ограничение, мне неизвестно.

3 голосов
/ 31 января 2019

UPDATE

протестировал его с Django 2.0.9 , он может автоматически определить, было ли поле переименовано, и дает возможность переименовать вместо удаления и создать новое. enter image description here

Первый

Публикация, если это все еще полезно для кого-то.

Для Django 2.0 + просто переименуйте поле в модели

class Foo(models.Model):
    orig_name = models.CharField(max_length=50)

до

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

Теперь запустите python manage.py makemigrations Будет сгенерирована миграция с операциями по удалению старого поля и добавлению нового.

Продолжайте и измените это на следующее.

operations = [
    migrations.RenameField(
        model_name='foo',
        old_name='orig_name',
        new_name='name')
]

Теперь запустите python manage.py migrate, он переименует столбец в БД без потери данных.

1 голос
/ 23 декабря 2015

Можно переименовать поле без ручного редактирования файла миграции:

  1. Добавить db_column = OLD_FIELD_NAME в исходное поле.
  2. Пробег: python3 manage.py makemigrations
  3. Переименуйте поле из OLD_FIELD_NAME в NEW_FIELD_NAME
  4. Прогон: python3 manage.py makemigrations

Вам будет предложено:

Переименовали ли вы МОДЕЛЬ . OLD_FIELD_NAME в МОДЕЛЬ . NEW_FIELD_NAME (ForeignKey)? [y / N] y

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

Эта процедура работает на Django 1.7 +.

1 голос
/ 11 ноября 2015

Я столкнулся с такой ситуацией на Django 1.7.7. Я закончил тем, что сделал следующее, которое работало для меня.

./manage.py makemigrations <app_name> --empty

Добавлен простой подкласс migrations.RenameField, который не касается базы данных:

class RenameFieldKeepDatabaseColumn(migrations.RenameField):
def database_backwards(self, app_label, schema_editor, from_state, to_state):
    pass

def database_forwards(self, app_label, schema_editor, from_state, to_state):
    pass
...