Подтвердить что ты не робот

Миграция изменения имени поля django-модели без потери данных

У меня есть проект django с таблицей базы данных, которая уже содержит данные. Я хотел бы изменить имя поля, не потеряв ни одного из данных в этом столбце. Мой первоначальный план состоял в том, чтобы просто изменить имя поля модели таким образом, чтобы на самом деле не изменять имя таблицы db (используя параметр столбца 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 вызывает перенос script, который удаляет исходный столбец orig_name и добавляет новый столбец name, который будет иметь нежелательный побочный эффект удаления данных в этом столбце, (Я также смущен тем, почему Юг хочет изменить имя столбца в db, поскольку мое понимание 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. удаление исходного столбца) Или я могу изменить миграцию script с чем-то вроде db.alter_column?

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

4b9b3361

Ответ 1

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

Добавление ответа для Django 1.8+ (с Django-родными переходами, а не с Югом).

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

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',
    ),
]

Ответ 2

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

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

Ответ 3

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

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

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

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

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

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

Ответ 4

Фактически с 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

Ответ 5

Я столкнулся с этой ситуацией на 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

Ответ 6

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

  • Добавить db_column = OLD_FIELD_NAME в исходное поле.
  • Запуск: python3 manage.py makemigrations
  • Переименуйте поле из OLD_FIELD_NAME в NEW_FIELD_NAME
  • Запуск: python3 manage.py makemigrations

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

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

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

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

Ответ 7

ОБНОВИТЬ

протестировал его с 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 он переименует столбец в БД без потери данных.