私は、既存のDjangoプロジェクトのいくつかのモデルの名前を変更する予定です。このプロジェクトには、名前を変更したいモデルと外部キー関係を持つ他の多くのモデルがあります。これには複数の移行が必要になると確信していますが、正確な手順はわかりません。
myapp
name__という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
name__モデルの名前を変更したいのは、名前が実際には意味をなさず、コードの混乱を引き起こしているためです。また、Bar
name__はより明確な名前を作成します。
Django開発ドキュメントで読んだ内容から、次の移行戦略を想定しています。
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
name__のfoo
name__フィールド名は変更されませんが、リレーションはBar
name__モデルに更新されます。私の理由は、一度にあまり変更しないで、このフィールド名をbar
name__に変更すると、その列のデータが失われる危険性があるからです。
空の移行を作成します。
python manage.py makemigrations --empty myapp
手順2で作成した移行ファイルのMigration
name__クラスを編集して、RenameModel
name__操作を操作リストに追加します。
class Migration(migrations.Migration):
dependencies = [
('myapp', '0001_initial'),
]
operations = [
migrations.RenameModel('Foo', 'Bar')
]
移行を適用します。
python manage.py migrate
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()
別の空の移行を作成します。
python manage.py makemigrations --empty myapp
手順6で作成した移行ファイルのMigration
name__クラスを編集して、関連するフィールド名のRenameField
name__操作を操作リストに追加します。
class Migration(migrations.Migration):
dependencies = [
('myapp', '0002_rename_fields'), # <-- is this okay?
]
operations = [
migrations.RenameField('AnotherModel', 'foo', 'bar'),
migrations.RenameField('YetAnotherModel', 'foo', 'bar')
]
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およびさらに古い移行ファイル(!)。
Update: ceasaro が言及しているように、Djangoの新しいバージョンは通常、モデルの名前が変更されました。最初にmanage.py makemigrations
を試してから、移行ファイルを確認してください。
最初は、ステップ4まで移行がうまくいったため、Fiverの方法がうまくいくと思いました。ただし、「ForeignKeyField(Foo)」から「ForeignKeyField(Bar)」への暗黙的な変更は、どの移行にも関係しませんでした。これが、リレーションシップフィールドの名前を変更しようとしたときに移行が失敗した理由です(ステップ5-8)。これは、私の場合、「AnotherModel」と「YetAnotherModel」が他のアプリでディスパッチされているという事実による可能性があります。
そこで、以下の手順に従ってモデルと関係フィールドの名前を変更しました。
this のメソッド、特にotranzerのトリックを採用しました。
Fiverのように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()
すべての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
モデル名を変更する
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
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移行への依存関係を追加する必要があります。したがって、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
最終的にはフィールドの名前を変更できます
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は、実際にモデル名を変更したかどうかを尋ねるはずです、yesと言います)
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を参照しました。
ここから: https://hanmir.wordpress.com/2012/08/30/rename-model-Django-south-migration/
Django 1.10の場合、Makemigrationsを実行するだけで2つのモデルクラス名(ForeignKeyとデータを含む)を変更し、アプリを移行しました。 Makemigrationsステップでは、テーブル名を変更することを確認する必要がありました。移行により、テーブルの名前が問題なく変更されました。
次に、ForeignKeyフィールドの名前を一致するように変更し、Makemigrationsから名前の変更を確認するように再度求められました。移行してから変更しました。
そのため、特別なファイル編集を行うことなく、2つのステップでこれを行いました。 @wasibigeekで述べたように、admin.pyファイルを変更するのを忘れたため、最初はエラーになりました。
Djangoバージョン1.9.4を使用しています
私は次の手順に従っています:
モデルの名前をoldNameからNewName Run python manage.py makemigrations
に変更しました。 Did you rename the appname.oldName model to NewName? [y/N]
select Yを要求します
python manage.py migrate
を実行すると、
次のコンテンツタイプは古く、削除する必要があります。
appname | oldName
appname | NewName
外部キーによってこれらのコンテンツタイプに関連するオブジェクトも削除されます。これらのコンテンツタイプを削除してもよろしいですか?不明な場合は、「いいえ」と答えてください。
Type 'yes' to continue, or 'no' to cancel: Select No
既存のすべてのデータの名前を変更し、新しい名前付きテーブルに移行します。
また、v.thoreyが説明したように問題に直面し、彼のアプローチは非常に有用ですが、ステップ1から4なしでファイバーが説明したように、ステップ7を変更する必要があることを除いて、実際にはステップ5から8のより少ないステップに凝縮できることを発見しました手順3の下。全体的な手順は次のとおりです。
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()
python manage.py makemigrations --empty myapp
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')
]
python manage.py migrate
追伸Django 1.9でこのアプローチを試しました
残念ながら、データベース内に古いテーブル名を残す名前変更移行に関する問題(それぞれDjango 1.x)が見つかりました。
Djangoは古いテーブルでは何も試さず、自分のモデルの名前を変更するだけです。一般的な外部キーとインデックスに関する同じ問題-そこの変更はDjangoによって適切に追跡されません。
最も簡単な解決策(回避策):
class Foo(models.Model):
name = models.CharField(unique=True, max_length=32)
...
Bar = Foo # and use Bar only
実際の解決策(すべてのインデックス、制約、トリガー、名前などを2回のコミットで切り替える簡単な方法ですが、smallerテーブルの場合):
Aをコミット:
# deprecated - TODO: TO BE REMOVED
class Foo(model.Model):
...
class Bar(model.Model):
...
Bar
のみで動作するようにコードを切り替えます。 (スキーマ上のすべての関係を含む)移行では、FooからBarにデータをコピーするRunPython
を準備します(Fooのid
を含む)
B:をコミット(ラッシュなし、チーム全体が移行されたときに実行)
Foo
さらにクリーンアップ:
djangoのバグ:
いくつかのテーブルの名前を変更する必要がありました。しかし、Djangoが気づいたモデルの名前は1つだけです。これは、Djangoがモデルの追加、削除を繰り返したために発生しました。各ペアについて、それらが同じアプリのものであり、 同一フィールド であるかどうかをチェックします。名前を変更するテーブルへの外部キーを持たないテーブルは1つだけです(覚えているように、外部キーにはモデルクラス名が含まれています)。つまり、1つのテーブルのみにフィールドの変更はありませんでした。それが気づいた理由です。
そのため、解決策は、一度に1つのテーブルの名前を変更し、models.py
(おそらくviews.py
)のモデルクラス名を変更し、移行を行うことです。その後、他の参照(モデルクラス名、関連(クエリ)名、変数名)についてコードを調べます。必要に応じて移行を行います。次に、オプションでこれらすべての移行を1つに結合します(インポートも必ずコピーしてください)。
私は@ceasaroの言葉を作ります。これについての彼のコメント answer です。
Djangoの新しいバージョンは、変更を検出し、何が行われたかを尋ねることができます。また、Djangoがいくつかの移行コマンドの実行順序を混在させる可能性があることも追加します。
小さな変更を適用してmakemigrations
およびmigrate
を実行し、エラーが発生した場合は移行ファイルを編集できます。
一部の行の実行順序は、エラーを回避するために変更できます。
PyCharmのような優れたIDEを使用している場合は、モデル名を右クリックしてリファクタリング->名前変更を実行できます。これにより、モデルを参照するすべてのコードを調べる手間が省けます。次に、makemigrationsを実行して移行します。 Django 2+は単に名前の変更を確認します。
Ceasaroコメントを確認して追加したかっただけです。 Django 2.0は現在、これを自動的に行うようです。
私はJango 2.2.1を使用しています。モデルの名前を変更してmakemigrationを実行するために必要なことはすべてしました。
ここで、特定のクラスの名前をAからBに変更したかどうかを確認し、yesを選択してmigrateを実行しましたが、すべて機能しているようです。
注:project/migrationsフォルダー内のファイルの古いモデル名は変更しませんでした。