Стратегия миграции Django для переименования полей модели и отношений

StackOverflow https://stackoverflow.com//questions/25091130

Вопрос

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

Допустим, я начинаю со следующих моделей в приложении Django под названием myapp:

class Foo(models.Model):
    name = models.CharField(unique=True, max_length=32)
    description = models.TextField(null=True, blank=True)


class AnotherModel(models.Model):
    foo = models.ForeignKey(Foo)
    is_awesome = models.BooleanField()


class YetAnotherModel(models.Model):
    foo = models.ForeignKey(Foo)
    is_ridonkulous = models.BooleanField()

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

Из того, что я прочитал в документации по разработке Django, я предполагаю следующую стратегию миграции:

Шаг 1

Изменить models.py:

class Bar(models.Model):  # <-- changed model name
    name = models.CharField(unique=True, max_length=32)
    description = models.TextField(null=True, blank=True)


class AnotherModel(models.Model):
    foo = models.ForeignKey(Bar)  # <-- changed relation, but not field name
    is_awesome = models.BooleanField()


class YetAnotherModel(models.Model):
    foo = models.ForeignKey(Bar)  # <-- changed relation, but not field name
    is_ridonkulous = models.BooleanField()

Обратите внимание AnotherModel имя поля для foo не меняется, но отношение обновляется до Bar модель.Я рассуждаю так: мне не следует менять слишком много сразу, и если я изменю это имя поля на bar Я бы рискнул потерять данные в этом столбце.

Шаг 2

Создайте пустую миграцию:

python manage.py makemigrations --empty myapp

Шаг 3

Отредактируйте Migration класс в файле миграции, созданном на шаге 2, чтобы добавить RenameModel операцию в список операций:

class Migration(migrations.Migration):

    dependencies = [
        ('myapp', '0001_initial'),
    ]

    operations = [
        migrations.RenameModel('Foo', 'Bar')
    ]

Шаг 4

Примените миграцию:

python manage.py migrate

Шаг 5

Отредактируйте имена связанных полей в models.py:

class Bar(models.Model):
    name = models.CharField(unique=True, max_length=32)
    description = models.TextField(null=True, blank=True)


class AnotherModel(models.Model):
    bar = models.ForeignKey(Bar)  # <-- changed field name
    is_awesome = models.BooleanField()


class YetAnotherModel(models.Model):
    bar = models.ForeignKey(Bar)  # <-- changed field name
    is_ridonkulous = models.BooleanField()

Шаг 6

Создайте еще одну пустую миграцию:

python manage.py makemigrations --empty myapp

Шаг 7

Отредактируйте Migration класс в файле миграции, созданном на шаге 6, чтобы добавить RenameField операция(и) для любых связанных имен полей в списке операций:

class Migration(migrations.Migration):

    dependencies = [
        ('myapp', '0002_rename_fields'),  # <-- is this okay?
    ]

    operations = [
        migrations.RenameField('AnotherModel', 'foo', 'bar'),
        migrations.RenameField('YetAnotherModel', 'foo', 'bar')
    ]

Шаг 8

Примените вторую миграцию:

python manage.py migrate

Помимо обновления остального кода (представлений, форм и т. д.) для отражения новых имен переменных, именно так будет работать новая функция миграции?

Кроме того, это кажется большим количеством шагов.Можно ли как-то сократить миграционные операции?

Спасибо!

Это было полезно?

Решение

Итак, когда я попробовал это, кажется, вы можете сократить шаги 3–7:

class Migration(migrations.Migration):

    dependencies = [
        ('myapp', '0001_initial'), 
    ]

    operations = [
        migrations.RenameModel('Foo', 'Bar'),
        migrations.RenameField('AnotherModel', 'foo', 'bar'),
        migrations.RenameField('YetAnotherModel', 'foo', 'bar')
    ]

Вы можете получить некоторые ошибки, если не обновите имена, в которые они импортированы, например.admin.py и даже более старые файлы миграции (!).

Обновлять:Как Чезаро Как упоминается, более новые версии Django обычно могут обнаружить и спросить, переименована ли модель.Поэтому постарайтесь manage.py makemigrations сначала, а затем проверьте файл миграции.

Другие советы

Сначала я думал, что метод Fiver сработал для меня, потому что миграция работала хорошо до шага 4.Однако неявные изменения 'ForeignKeyField(Foo)' в 'ForeignKeyField(Bar)' не были связаны ни с какими миграциями.Вот почему миграция завершилась неудачей, когда я захотел переименовать поля отношений (шаг 5-8).Это может быть связано с тем, что мои "AnotherModel" и "YetAnotherModel" в моем случае отправляются в другие приложения.

Итак, мне удалось переименовать свои модели и поля отношений, выполнив следующие действия:

Я адаптировал этот метод из это и особенно трюк отранзера.

Итак, как и Fiver, давайте предположим, что у нас есть в мое приложение:

class Foo(models.Model):
    name = models.CharField(unique=True, max_length=32)
    description = models.TextField(null=True, blank=True)

И в myotherapp - мое другое приложение:

class AnotherModel(models.Model):
    foo = models.ForeignKey(Foo)
    is_awesome = models.BooleanField()


class YetAnotherModel(models.Model):
    foo = models.ForeignKey(Foo)
    is_ridonkulous = models.BooleanField()

Шаг 1:

Преобразуйте каждое OneToOneField(Foo) или ForeignKeyField(Foo) в IntegerField().(При этом идентификатор связанного объекта Foo будет сохранен в качестве значения integerfield).

class AnotherModel(models.Model):
    foo = models.IntegerField()
    is_awesome = models.BooleanField()

class YetAnotherModel(models.Model):
    foo = models.IntegerField()
    is_ridonkulous = models.BooleanField()

Тогда

python manage.py makemigrations

python manage.py migrate

Шаг 2:(Как в шаге 2-4 из Fiver)

Измените название модели

class Bar(models.Model):  # <-- changed model name
    name = models.CharField(unique=True, max_length=32)
    description = models.TextField(null=True, blank=True)

Создайте пустую миграцию:

python manage.py makemigrations --empty myapp

Затем отредактируйте его следующим образом:

class Migration(migrations.Migration):

    dependencies = [
        ('myapp', '0001_initial'),
    ]

    operations = [
        migrations.RenameModel('Foo', 'Bar')
    ]

В конце концов

python manage.py migrate

Шаг 3:

Преобразуйте обратно ваше IntegerField() в их предыдущее ForeignKeyField или OneToOneField, но с новой моделью Bar.(Предыдущее целочисленное поле хранило идентификатор, поэтому django понимает это и восстанавливает соединение, что круто.)

class AnotherModel(models.Model):
    foo = models.ForeignKey(Bar)
    is_awesome = models.BooleanField()

class YetAnotherModel(models.Model):
    foo = models.ForeignKey(Bar)
    is_ridonkulous = models.BooleanField()

Тогда делай:

python manage.py makemigrations 

Очень важно, что на этом шаге вы должны изменять каждую новую миграцию и добавлять зависимость от RenameModel Foo-> Bar миграции.Итак, если и AnotherModel, и YetAnotherModel находятся в myotherapp, созданная миграция в myotherapp должна выглядеть следующим образом:

class Migration(migrations.Migration):

    dependencies = [
        ('myapp', '00XX_the_migration_of_myapp_with_renamemodel_foo_bar'),
        ('myotherapp', '00xx_the_migration_of_myotherapp_with_integerfield'),
    ]

    operations = [
        migrations.AlterField(
            model_name='anothermodel',
            name='foo',
            field=models.ForeignKey(to='myapp.Bar'),
        ),
        migrations.AlterField(
            model_name='yetanothermodel',
            name='foo',
            field=models.ForeignKey(to='myapp.Bar')
        ),
    ]

Тогда

python manage.py migrate

Шаг 4:

В конце концов вы сможете переименовать свои поля

class AnotherModel(models.Model):
    bar = models.ForeignKey(Bar) <------- Renamed fields
    is_awesome = models.BooleanField()


class YetAnotherModel(models.Model):
    bar = models.ForeignKey(Bar) <------- Renamed fields
    is_ridonkulous = models.BooleanField()

а затем выполните автоматическое переименование

python manage.py makemigrations

(django должен спросить вас, действительно ли вы переименовали modelname, скажите "да")

python manage.py migrate

И это все!

Это работает на Django1.8

Мне нужно было сделать то же самое.Я изменил модель все сразу (то есть шаг 1 и шаг 5 вместе).Затем создал миграцию схемы, но отредактировал это:

class Migration(SchemaMigration):
    def forwards(self, orm):
        db.rename_table('Foo','Bar')

    def backwards(self, orm):
        db.rename_table('Bar','Foo')
.

Это работало прекрасно.Все мои существующие данные появились, все остальные таблицы ссылаются на бар отлично.

Отсюда: https:// hanmir.wordpress.com/2012/08/30/romage-model-django-south-migration/

Для Django 1.10 мне удалось изменить два имена классов модели (включая инострудку, а также с данными), просто работая макемиграциями, а затем мигрируйте для приложения.Для шага Makemigrations я должен был подтвердить, что я хотел изменить имена таблиц.Миграция изменила имена таблиц без проблем.

Тогда я изменил имя поля Инонарна, чтобы соответствовать, и снова просили Makemigrations, чтобы подтвердить, что я хотел изменить имя.Мигрировать, чем сделал изменение.

Поэтому я взял это на два шага без какого-либо специального редактирования файлов.Сначала я получил ошибки, потому что забыл изменить файл admin.py, как указано @wasibigek.

Я также столкнулся с проблемой, как V. Это описано и обнаружил, что его подход очень полезен, но может быть конденсирован в меньшего количества шагов, которые на самом деле шагаются от 5 до 8, что и у пьев, описанной без шага, за исключением того, что этот шаг 7 должен бытьИзменено как мой шаг ниже 3. Общие шаги следуют следующим образом:

Шаг 1: отредактируйте имена соответствующих полей в моделях .py

class Bar(models.Model):
    name = models.CharField(unique=True, max_length=32)
    description = models.TextField(null=True, blank=True)


class AnotherModel(models.Model):
    bar = models.ForeignKey(Bar)  # <-- changed field name
    is_awesome = models.BooleanField()


class YetAnotherModel(models.Model):
    bar = models.ForeignKey(Bar)  # <-- changed field name
    is_ridonkulous = models.BooleanField()
.

Шаг 2: Создайте пустую миграцию

python manage.py makemigrations --empty myapp
.

Шаг 3: Отредактируйте класс миграции в файле миграции, созданный на шаге 2

class Migration(migrations.Migration):

dependencies = [
    ('myapp', '0001_initial'), 
]

operations = [
    migrations.AlterField(
        model_name='AnotherModel',
        name='foo',
        field=models.IntegerField(),
    ),
    migrations.AlterField(
        model_name='YetAnotherModel',
        name='foo',
        field=models.IntegerField(),
    ),
    migrations.RenameModel('Foo', 'Bar'),
    migrations.AlterField(
        model_name='AnotherModel',
        name='foo',
        field=models.ForeignKey(to='myapp.Bar'),
    ),
    migrations.AlterField(
        model_name='YetAnotherModel',
        name='foo',
        field=models.ForeignKey(to='myapp.Bar'),
    ),
    migrations.RenameField('AnotherModel', 'foo', 'bar'),
    migrations.RenameField('YetAnotherModel', 'foo', 'bar')
]
.

Шаг 4: Примените миграцию

python manage.py migrate
.

сделано

p.s.Я пробовал этот подход на Django 1.9

Я использую django версию 1.9.4

Я выполняю следующие шаги: -

Я просто переименул модель Adlyname в newname Запустите python manage.py makemigrations.Это спросит вас Did you rename the appname.oldName model to NewName? [y/N] Выберите Y

Запустите python manage.py migrate, и он попросит вас заняться

Следующие типы содержимого являются устаревшими и должны быть удалены:

appname | oldName
appname | NewName
.

Любые объекты, связанные с этими типами содержимого по внешнему ключу, также быть удаленным.Вы уверены, что хотите удалить эти типы контента? Если вы не уверены, ответьте «Нет».

Type 'yes' to continue, or 'no' to cancel: Select No
.

Это переименовывает и переносит все существующие данные в новую именованную таблицу для меня.

К сожалению, я обнаружил проблемы (каждая версия django 1.x) с переименованием миграции, из-за которых в базе данных остаются старые имена таблиц.

Джанго даже не пробует ничего на старой таблице, просто переименовывает свою модель.Та же проблема с внешними ключами и индексами в целом - изменения там не отслеживаются должным образом Django.

Самое простое решение (обходной путь):

class Foo(models.Model):
     name = models.CharField(unique=True, max_length=32)
     ...
Bar = Foo  # and use Bar only

Реальное решение (простой способ переключения всех индексов, ограничений, триггеров, имен и т. Д. меньше таблицы):

совершить А:

  1. создать такой же модель как старая
# deprecated - TODO: TO BE REMOVED
class Foo(model.Model):
    ...

class Bar(model.Model):
    ...
  1. переключите код для работы с новой моделью Bar только.(включая все отношения на схеме)

В процессе миграции подготовьтесь RunPython, которые копируют данные из Foo to Bar (включая id из Фу)

  1. необязательная оптимизация (при необходимости для больших таблиц)

зафиксировать Б: (не торопитесь, сделайте это, когда будет перенесена вся команда)

  1. безопасное падение старой модели Foo

дальнейшая очистка:

  • сквош на миграциях

ошибка в Джанго:

Мне нужно было переименовать пару таблиц.Но только одна модель переименования была замечена Джанго.Это произошло потому, что Django Iterate по сравнению с добавлением, затем удалил модели.Для каждой пары он проверяет, если они имеют одно и то же приложение и имеют Одинаковые поля .Только один стол не имел иностранных клавиш, чтобы быть переименованы в таблицы (внешние ключевые ключи содержат имя модели, как вы помните).Другими словами, только один стол не имел изменения поля.Вот почему это было замечено.

Итак, решение - переименовать одну таблицу за раз, изменяя название класса модели в models.py, возможно, Renacodicetacodcode и выполнение миграции.После этого проверьте ваш код для других ссылок (названия классов модели, связанные с именами, имена переменной).Сделайте миграцию, если это необходимо.Затем необязательно объединить все эти миграции в одну (обязательно скопируйте импорт).

Я бы сделал слова @Ceasaro, мои на его комментарии на этом Ответ .

Новые версии Django могут обнаружить изменения и спросить о том, что было сделано. Я также добавил, что Django может смешивать порядок выполнения некоторых команд миграции.

Было бы разумно применить небольшие изменения и запустить makemigrations и migrate, и если ошибка возникает, файл миграции может быть отредактирован.

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

Just wanted to confirm and add upon ceasaro comment. Django 2.0 seems to do this automatically now.

I'm on Jango 2.2.1, all I had to do what to rename the model and run makemigrations.

Here it asks if I had renamed the specific class from A to B, I chose yes and ran migrate and all seems to work.

Note I did not rename the old model name in any files inside the project/migrations folder.

If you are using a good IDE like PyCharm you can right click on the model name and do a refactor -> rename. This saves you the trouble of going through all your code that references the model. Then run makemigrations and migrate. Django 2+ will simply confirm name change.

I upgraded Django from version 10 to version 11:

sudo pip install -U Django

(-U for "upgrade") and it solved the problem.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top