モデルと関係の分野の名前変更のためのDjango移行戦略
-
02-01-2020 - |
質問
名前の変更を希望するモデルに外部キー関係を持つ他の多くのモデルがある既存のDjangoプロジェクトでいくつかのモデルの名前を変更することを計画しています。私はかなり確実にこれが複数の移行を必要とするでしょうが、正確な手順がよくわからない。
myapp
というDjangoアプリ内で次のモデルを使って起動しましょう。
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()
.
Note AnotherModel
のfoo
フィールド名は変わりませんが、関係はBar
モデルに更新されます。私の推論は、私が一度に変更しすぎず、このフィールド名をbar
に変更した場合、その列内のデータを失う危険性があることです。
ステップ2
空の移行を作成する:
python manage.py makemigrations --empty myapp
.
ステップ3
手順2で作成した移行ファイルでMigration
クラスを編集して、演算リストに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
手順6で作成した移行ファイル内のMigration
クラスを編集して、任意のフィールド名を任意のフィールド名に演算リストに追加します。
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
.
新しい変数名を反映するためのコードの残りの部分(ビュー、フォームなど)を更新することは、これは基本的に新しい移行機能がどのように機能するかですか?
また、これは多くのステップのようです。移行操作は何らかの方法で集められますか?
ありがとう!
解決
だから私がこれを試したとき、それはあなたが集まることができるようですステップ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
を試してから移行ファイルを確認してください。
他のヒント
最初に、移行がステップ4までうまく機能しているため、Fiverの方法は私のために働いていたと思いました。リレーションシップフィールドの名前を変更したいと思ったときにマイグレーションが失敗したのはこのためです(ステップ5-8)。 これは、私の「忠実なモーデル」と「浮遊物質」が私の症例の他のアプリで発送されているという事実によるかもしれません。
だから私は以下の手順に従って私のモデルと関係のフィールドの名前を変更することができました:
私は方法をこの、特にオトランザーのトリック。
を持っていると言ってみましょう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()
.
ステップ1:
OneTooneField(foo)またはForeignKeyField(foo)をIntegerField()に変換します。 (これにより、関連するFooオブジェクトのIDが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のように)
モデル名を変更する
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にバックバックしますが、新しいバーモデルを使用して。 (前のIntegerFieldはIDを保存していたので、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の依存関係を追加する必要があります。 そのため、MyotherAppの作成された移行は、MyotherAppの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
.
(あなたが実際にモデル名と名前を変更した場合は、はい)
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/30/rename-model-django-south-migration/
Django 1.10の場合、MakeMigrationsを実行するだけで、2つのモデルクラス名(外部キーを含む)を変更してからアプリの移行を管理しました。MakeMigrationsステップの場合は、テーブル名を変更したいことを確認しなければなりませんでした。マイグレーションは、テーブルの名前を問題なく変更しました。
その後、Foregeykeフィールドの名前を一致させ、再度MakeMigrationsから尋ねられて名前を変更したいことを確認しました。変更を行ったよりも移行します。
だから私は特別なファイル編集なしで2つのステップでこれを取りました。@wasibigeekeによって言及されているように、admin.pyファイルを変更するのを忘れたので、最初にエラーを取得しました。
私はまた、v当社が説明したように問題に直面し、彼のアプローチが非常に有用であるが実際にステップ5~8のより少ないステップに凝縮することができるが、ステップ7を必要とする必要があることを除いて、実際にステップ5~8のより少ないステップに凝縮することができる。以下のステップ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)がありました。
Djangoさえ古いテーブルで何も試しても、自分のモデルの名前を変更するだけです。 外部キーと同じ問題、および一般的なインデックス - Djangoによって正しく追跡されていません。
最も簡単な解決策(回避策):
class Foo(models.Model):
name = models.CharField(unique=True, max_length=32)
...
Bar = Foo # and use Bar only
.
実際の解決策(すべて切り替える簡単な方法 2コミットではなく、小さいテーブルの場合はむしろインデックス、制約、トリガー、名前などはむしろ
コミットA:
- 同じモデルを古いものとして作成します
- 新しいモデル
Bar
のみで動作するようにコードを切り替えます。 (スキーマ上のすべての関係を含む) - オプションの最適化(より大きなテーブルに必要な場合)
- 古いモデル
Foo
の安全なドロップ
- 移行時のスカッシュ
# deprecated - TODO: TO BE REMOVED
class Foo(model.Model):
...
class Bar(model.Model):
...
.
移行では、FooからBarにデータをコピーするRunPython
を準備する
(Fooのid
を含む)
COMMIT B:(チーム全体が移行されたときにそれをする)
さらにクリーンアップ:
Djangoのバグ:
私は2つのテーブルの名前を変更する必要がありました。しかし、Djangoによって注目されていたモデル名のみが1つだけでした。それは、Djangoが追加された後に反復し、次にモデルを削除したために起こりました。ペアごとに、同じアプリであるかどうかを確認し、同一フィールド。名前を変更する表の外部キーが1つだけありません(外部キーには、覚えているようにモデルクラス名が含まれています)。言い換えれば、フィールドが変更されていないテーブルのみが1つだけです。それがそれが気づいた理由です。
そのため、解決策は一度に1つのテーブルの名前を変更し、models.py
、おそらくviews.py
、および移行を行うことです。その後、コードを他の参照(モデルクラス名、関連(クエリ)名、変数名)について検査してください。必要に応じてマイグレーションを行います。その後、これらすべてのマイグレーションを1つに1つに組み合わせることで、[インポートをコピーしてください)。
私は@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.