Стратегия миграции 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 и даже более старые файлы миграции (!).
обновление: как ceasaro упоминает, что более новые версии Django обычно могут обнаруживать и спрашивать, переименована ли модель. Так что попробуйте
manage.py makemigrationsсначала проверьте файл миграции.
сначала я думал, что метод Fiver работал для меня, потому что миграция работала хорошо до шага 4. Однако неявные изменения ' ForeignKeyField(Foo)' в 'ForeignKeyField (Bar)' не были связаны ни с какими миграциями. Вот почему миграция не удалась, когда я хотел переименовать поля отношений (шаг 5-8). Это может быть связано с тем, что мои "AnotherModel" и "YetAnotherModel" отправляются в других приложениях в моем случае.
так что мне удалось переименовать мои модели и отношения поля, выполняющие следующие действия:
я адаптировал метод из этой и особенно трюк отранцера.
так как пятерка допустим у нас есть в приложение:
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 в качестве значения целочисленного поля).
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 от пятерки)
изменить название модели
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 но с помощью новая модель бара. (Предыдущее целочисленное поле хранило идентификатор, поэтому 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 migrations. Поэтому, если 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 должен спросить вас, действительно ли вы переименовали имя модели, скажите да)
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/rename-model-django-south-migration/
для Django 1.10 мне удалось изменить два имени класса модели (включая ForeignKey и с данными), просто запустив Makemigrations, а затем мигрировать для приложения. Для шага Makemigrations мне нужно было подтвердить, что я хочу изменить имена таблиц. Миграция изменила имена таблиц без проблем.
затем я изменил имя поля ForeignKey, чтобы соответствовать, и снова попросил Makemigrations подтвердить, что я хочу изменить имя. Миграции, чем сделать изменения.
поэтому я сделал это в два этапа без какого-либо специального редактирования файлов. Сначала я получил ошибки, потому что забыл изменить admin.py файл, как упоминалось @wasibigeek.
Я также столкнулся с проблемой, как описал В. тори, и обнаружил, что его подход очень полезен, но может быть сконденсирован в меньшее количество шагов, которые на самом деле являются шагом 5-8, как описано в Fiver без шага 1-4, за исключением того, что Шаг 7 должен быть изменен как мой ниже Шаг 3. Общие шаги заключаются в следующем:
Шаг 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выполнить
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 перебирает добавленные, а затем удаленные модели. Для каждой пары он проверяет, являются ли они одним и тем же приложением и имеют идентичные поля. Только одна таблица не имела внешних ключей к таблицам, которые нужно переименовать (внешние ключи содержат имя класса модели, как вы помните). Другими словами, только одна таблица не имела изменений полей. Вот почему это было замечено.
таким образом, решение чтобы переименовать одну таблицу за раз, измените имя класса модели в
models.py, возможноviews.py, и совершая миграции. После этого проверьте свой код на наличие других ссылок (имена классов моделей, связанные (запрос) имена, имена переменных). Сделайте миграцию, если это необходимо. Затем при необходимости объедините все эти миграции в одну (обязательно скопируйте импорт).
я бы сделал @ceasaro слова, Мои на его комментарий по этому поводу ответ.
новые версии Django могут обнаруживать изменения и спрашивать о том, что было сделано. Я также добавил бы, что Django может смешивать порядок выполнения некоторых команд миграции.
было бы разумно применить небольшие изменения и запустить
makemigrationsиmigrateи если возникает ошибка, файл миграции можно редактировать.некоторые строки порядок выполнения может быть изменен, чтобы избежать и омеги.
я обновил Django с версии 10 до версии 11:
sudo pip install -U Django(
-Uдля "апгрейда") и это решило проблему.
Comments