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

я планирую переименовать несколько моделей в существующем проекте 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

применить 2-ой миграции:

python manage.py migrate

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

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

спасибо!

10 ответов


поэтому, когда я попробовал это, кажется, вы можете конденсировать Шаг 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 и даже более старые файлы миграции (!).

обновление: As ceasaro упоминает, более новые версии Django обычно могут обнаружить и спросить, переименована ли модель. Так что попробуйте manage.py makemigrations сначала проверьте файл миграции.


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

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

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

так как пятерка, скажем, у нас есть в myapp:

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:

преобразуйте каждое OntToOneField(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:

преобразуйте свое целочисленное поле() в их предыдущее ForeignKeyField, OneToOneField, но с помощью новая модель бара. (Предыдущее целочисленное поле хранило идентификатор, поэтому 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')

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

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


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

затем я изменил имя поля ForeignKey на соответствующее, и снова Makemigrations попросили подтвердить, что я хочу изменить имя. Миграции, чем сделать изменения.

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


Шаг 1: измените связанные имена полей в 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()

Шаг 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

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

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

Run python manage.py migrate и он попросит вас

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

appname | oldName
appname | NewName

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

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

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


к сожалению, я нашел проблемы (каждый django 1.x) с переименованием миграции, которые оставляют старые имена таблиц в БД (даже не пытайтесь ничего на старой таблице, просто переименуйте модель).

решение для этого случая:

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

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

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


Я бы сделал @ceasaro слова, Мои на его комментарий по этому ответ.

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

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

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


я обновил Django с версии 10 до версии 11:

sudo pip install -U Django

(-U для "апгрейда") и это решило проблему.