モデルおよび関係フィールドの名前を変更するためのDjango移行戦略


152

名前を変更したいモデルとの外部キー関係を持つ他の多くのモデルが存在する既存の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

新しい変数名を反映するように残りのコード(ビュー、フォームなど)を更新する以外に、これは基本的に新しい移行機能がどのように機能するのでしょうか?

また、これは多くのステップのようです。移行操作を何らかの方法で凝縮できますか?

ありがとう!

回答:


125

だから私がこれを試したとき、あなたはステップ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最初に試してから、移行ファイルを確認してください。


答えてくれてありがとう。私は概要を説明した手順を使用して移行してきましたが、これを既存のデータで試したのか、空のデータベースで試したのか知りたいです。
2014年

2
ローカル環境のsqliteに数行しかありませんが、既存のデータで試してみました(
本番環境に

4
マイグレーションファイルで使用する場合は、マイグレーションファイルのモデル名を変更する必要はありませんapps.get_model。それを理解するのに多くの時間がかかりました。
アーメド

9
django 2.0では、モデル名を変更すると、./manage.py makemigrations myappコマンドはモデルの名前を変更したかどうかを尋ねてきます。例:myapp.Fooモデルの名前をBarに変更しましたか?[y / N] 「y」と答えると、移行migration.RenameModel('Foo', 'Bar')には名前が変更されたフィールドの同じカウントが含まれます:-)
ceasaro

1
manage.py makemigrations myappそれでも失敗する可能性があります:「モデルの名前とそのかなりの数のフィールドを一度に変更する場合、これを手動で追加する必要がある場合があります。自動検出では、これは古い名前のモデルを削除して新しいモデルを追加したように見えます別の名前を付けると、作成される移行によって古いテーブルのデータが失われます。」Django 2.1ドキュメント 私にとっては、空のマイグレーションを作成し、モデル名をそれに追加して、makemigrations通常どおり実行するだけで十分でした。
hlongmore

36

最初は、移行がステップ4までうまく機能していたため、Fiverの方法がうまくいくと思いました。しかし、暗黙的な変更「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:

すべての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:(Fiverのステップ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マイグレーションへの依存関係を追加する必要があります。したがって、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で動作します


3
ありがとうございました!それは非常に役に立ちました。しかし、メモ-Fooの名前をBarに変更した後、Barという名前の新しいモデルを作成したため、PostgreSQLフィールドインデックスの名前を変更または削除する必要がありました。
アナトリーシェルバコフ2015年

これありがとう!キーの部分は、名前を変更するモデル内外のすべての外部キーをに変換していると思いますIntegerField。これは私にとっては完璧に機能し、正しい名前で再作成されるという利点もあります。もちろん、実際に実行する前にすべてのマイグレーションを確認することをお勧めします!
zelanix 2016

ありがとうございました!他のモデルが外部キーを持っているモデルの名前を変更するために多くの異なる戦略を試しました(手順1〜3)。これが機能したのはこれだけでした。
MSH

変更ForeignKeyに秒IntegerFieldのことは今日の私の一日を救いました!
メフメット

8

私は同じことをして従う必要がありました。一度にモデルを変更しました(Fiverの回答からステップ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/


共有していただきありがとうございます。その答えが役に立った場合は、必ずwasibigeekを+1してください。
ドル紙幣

7

Django 1.10の場合、Makemigrationsを実行してアプリのMigrateを実行するだけで、2つのモデルクラス名(ForeignKeyを含む、データ付き)を変更できました。Makemigrationsステップでは、テーブル名を変更することを確認する必要がありました。移行は問題なくテーブルの名前を変更しました。

次に、ForeignKeyフィールドの名前を一致するように変更し、もう一度Makemigrationsから名前を変更することを確認するように求められました。変更を加えてから移行します。

そのため、特別なファイル編集をせずに、これを2つのステップで実行しました。@wasibigeekで言及されているように、admin.pyファイルを変更するのを忘れたため、最初はエラーが発生しました。


どうもありがとう!Django 1.11にも
Francisco Francisco

5

v.thoreyが説明するように私も問題に直面し、彼のアプローチは非常に有用であることがわかりましたが、実際には手順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で作成した移行ファイルのMigrationクラスを編集する

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

できた

PS私はDjango 1.9でこのアプローチを試しました


5

Djangoバージョン1.9.4を使用しています

私は次の手順に従っています:-

モデルの名前をoldNameからNewName Runに変更しましたpython manage.py makemigrationsDid 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

すべての既存のデータの名前を変更して、新しい名前付きテーブルに移行します。


何が「ノー」打った後ときに起こっていないので、感謝の男は、私は混乱していた
farhawa

3

残念ながら、データベースに古いテーブル名を残す名前変更の移行で問題(各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:

  1. 古いモデルと同じモデルを作成します
# deprecated - TODO: TO BE REMOVED
class Foo(model.Model):
    ...

class Bar(model.Model):
    ...
  1. 新しいモデルでBarのみ動作するようにコードを切り替えます。(スキーマ上のすべての関係を含む)

移行準備ではRunPython、FooからBar(Fooを含むid)にデータをコピーします

  1. オプションの最適化(より大きなテーブルに必要な場合)

コミットB:(急いではなく、チーム全体が移行されたときに実行してください)

  1. 古いモデルの安全なドロップ Foo

さらにクリーンアップ:

  • 移行時にスカッシュ

Djangoのバグ:


3

ceasaroコメントを確認して追加したかっただけです。Django 2.0はこれを自動的に行うようになりました。

私はDjango 2.2.1を使用していますmakemigrations。モデルの名前を変更して実行するために必要なことはすべてです。

ここで、特定のクラスの名前をからAに変更したかどうかを尋ねられBます。「はい」を選択して移行を実行すると、すべてが機能するようです。

注:project / migrationsフォルダー内のファイルで、古いモデル名を変更していません。


1

いくつかのテーブルの名前を変更する必要がありました。しかし、Djangoが気づいたのは1つのモデルの名前変更だけです。これは、Djangoが追加されたモデルと削除されたモデルを繰り返し処理するために発生しました。各ペアについて、それらが同じアプリであり、同じフィールドを持っているかどうかを確認します。名前を変更するテーブルへの外部キーがないテーブルは1つだけです(覚えているように、外部キーにはモデルクラス名が含まれています)。つまり、フィールドの変更がなかったテーブルは1つだけです。それが気づかれた理由です。

したがって、解決策は、一度に1つのテーブルの名前を変更し、でモデルクラス名を変更しmodels.py、場合によってviews.pyはを移行して、移行を行うことです。その後、他の参照(モデルクラス名、関連する(クエリ)名、変数名)についてコードを検査します。必要に応じて、移行を行います。次に、必要に応じて、これらすべての移行を1つに結合します(必ずインポートもコピーしてください)。


1

私は@ceasaroの言葉を作ります、この答えに関する彼のコメントを私のものにします

Djangoの新しいバージョンでは、変更を検出して、何が行われたかを尋ねることができます。また、Djangoが一部の移行コマンドの実行順序を混在させる可能性があることも付け加えておきます。

それは小さな変更を適用して実行するのが賢明だろうmakemigrationsmigrate、エラーが発生した場合、移行ファイルを編集することができます。

一部の行の実行順序は、エラーを回避するために変更できます。


モデル名を変更し、外部キーが定義されている場合など、これは機能しないことに注意してください...
Dean Kayton

以前のコメントを拡張:モデル名を変更してmakemigrationsを実行するだけの場合、foreignkeysなどで 'NameError:name' <oldmodel> 'is not defined'と表示されます...これを変更してmakemigrationsを実行すると、インポートエラーが発生しますadmin.pyで...それを修正してmakemigrationsを再度実行すると、「<app.oldmodel>モデルの名前を<newmodel>に変更しましたか?」というプロンプトが表示されますが、マイグレーションを適用すると、「ValueError:The field <app .newmodel.field1>は '<app.oldmodel>'への遅延参照で宣言されましたが、アプリ '<app>'はモデル '<oldmodel>'などを提供しません... '
Dean Kayton

このエラーは、過去の移行で参照の名前を変更する必要があるようです。
mhatch

@DeanKaytonはそれmigrations.SeparateDatabaseAndStateが助けになると言うでしょうか?
diogosimao

1

PyCharmのような優れたIDEを使用している場合は、モデル名を右クリックして、リファクタリング->名前の変更を行うことができます。これにより、モデルを参照するすべてのコードを処理する手間が省けます。次にmakemigrationsを実行して移行します。Django 2+は単に名前の変更を確認します。


-10

Djangoをバージョン10からバージョン11にアップグレードしました。

sudo pip install -U Django

-U「アップグレード」用)そしてそれは問題を解決しました。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.