web-dev-qa-db-ja.com

データを失うことなくDjango-modelフィールド名の変更を移行する

Djangoプロジェクトには、すでにデータが含まれているデータベーステーブルがあります。その列のデータを失わずにフィールド名を変更したいと思います。私の元々の計画は、実際にデータベーステーブルの名前を変更しない方法でのモデルフィールド名(db_column列パラメーターを使用):

元のモデル:

class Foo(models.Model):
  orig_name = models.CharField(max_length=50)

新しいモデル:

class Foo(models.Model):
  name = models.CharField(max_length=50, db_column='orig_name')

ただし、Southのschemamigration --autoを実行すると、元の列orig_nameを削除し、新しい列nameを追加する移行スクリプトが生成されます。これにより、その列。 (データベーステーブルの列の名前を変更せずにモデルフィールド名を変更できるというのがdb_columnの理解であったため、Southがdbの列の名前を変更する理由についても混乱しています)。

Dbフィールドを変更せずにモデルフィールドを変更しても問題が解決しない場合は、次のように名前をもっと簡単に変更できると思います。

元のモデル:

class Foo(models.Model):
  orig_name = models.CharField(max_length=50)

新しいモデル:

class Foo(models.Model):
  name = models.CharField(max_length=50)

最終的にどの戦略を使用するかに関係なく(最初の方法を優先しますが、2番目の方法は許容できると思います)、私の主な関心事は、その列に既にあるデータを失わないようにすることです。

これには複数ステップのプロセスが必要ですか? (1.列の追加、2。古い列から新しい列へのデータの移行、3。元の列の削除など)または、db.alter_columnのようなもので移行スクリプトを変更できますか?

列の名前を変更しながらその列のデータを保持するための最良の方法は何ですか?

43
sfletche

DBフィールドを維持したままフィールド名を変更する

Django 1.8+の回答を追加します(Djangoネイティブの移行ではなく、Southではありません)。

最初にdb_columnプロパティを追加し、次にフィールドの名前を変更する移行を行います。 Djangoは、最初は何もしない(db_columnを変更しないため)であり、2番目は何もしない(スキーマを作成しないため)と理解しています実際にログを調べて、スキーマの変更がないことを確認しました...

operations = [
    migrations.AlterField(
        model_name='mymodel',
        name='oldname',
        field=models.BooleanField(default=False, db_column=b'oldname'),
    ),
    migrations.RenameField(
        model_name='mymodel',
        old_name='oldname',
        new_name='newname',
    ),
]
40

修正は非常に簡単です。ただし、マイグレーションを自分で変更する必要があります。

列を削除して追加する代わりに、 db.rename_column を使用します。 schemamigration --autoによって作成された移行を変更するだけです。

17
Wolph

更新2

Django 2.2でも同じように機能します

更新1

Django 2.0.9でテストしました。フィールドの名前が変更されたかどうかを自動的に検出し、削除して新しいフィールドを作成する代わりに名前を変更するオプションを提供します- enter image description here

最初の答え

投稿がまだ役立つ場合。

Django 2.0の場合+モデルのフィールドの名前を変更するだけ

class Foo(models.Model):
    orig_name = models.CharField(max_length=50)

class Foo(models.Model):
    name = models.CharField(max_length=50)

python manage.py makemigrations古いフィールドを削除して新しいフィールドを追加する操作を含む移行が生成されます。

先に進んで、それを次のように変更してください。

operations = [
    migrations.RenameField(
        model_name='foo',
        old_name='orig_name',
        new_name='name')
]

python manage.py migrateデータを失うことなく、DBの列の名前を変更します。

11
Aarif

実際にDjango 1.10を使用して、モデルのフィールドの名前を変更してからmakemigrationsを実行すると、操作がすぐに識別されます(つまり、1つのフィールドが消え、別のフィールドが代わりに表示されます)。

$ ./manage.py makemigrations
Did you rename articlerequest.update_at to articlerequest.updated_at (a DateTimeField)? [y/N] y
Migrations for 'article_requests':
  article_requests/migrations/0003_auto_20160906_1623.py:
    - Rename field update_at on articlerequest to updated_at

私はこの状況に遭遇しました。モデルのフィールド名を変更したいが、列名は同じにしたかった。

私がやった方法は、schemamigration --empty [app] [some good name for the migration]を実行することです。問題は、Southに関する限り、モデル内のフィールド名の変更は、処理する必要がある変更であるということです。したがって、移行を作成する必要があります。ただし、データベース側で実行する必要があることは何もないことはわかっています。 したがって、空の移行はデータベースで不必要な操作を実行することを避け、それでも変更と見なされるものを処理するサウスのニーズを満たします。

loaddataを使用するか、Djangoのテストフィクスチャ機能(裏でloaddataを使用)を使用する場合は注意してください。フィクスチャはデータベースフィールド名ではなくモデルフィールド名に基づいているため、新しいフィールド名を使用するようにフィクスチャを更新する必要があります。

データベースで列名が変更される場合、列の移行にdb.rename_columnを使用することはお勧めしません。私は この答え でsjhによって記述された方法を使用します:

新しい列を1つのschemamigrationとして追加し、次にデータ移行を作成して値を新しいフィールドに移動し、2番目のschemamigrationで古い列を削除しました

その質問の コメント で述べたように、db.rename_columnの問題は、列と一緒に制約の名前を変更しないことです。問題が単なる表面的なものであるか、それとも制約が見つからないために将来の移行が失敗する可能性があるかは、私には不明です。

5
Louis

手動で移行ファイルを編集せずにフィールドの名前を変更することが可能です。

  1. 元のフィールドにdb_column = OLD_FIELD_NAMEを追加します。
  2. 実行:python3 manage.py makemigrations
  3. フィールドの名前をOLD_FIELD_NAMEからNEW_FIELD_NAMEに変更します
  4. 実行:python3 manage.py makemigrations

プロンプトが表示されます:

[〜#〜] model [〜#〜] .OLD_FIELD_NAME[〜#〜] model [〜#〜]に名前変更しましたか? NEW_FIELD_NAME(a ForeignKey)? [y/N] y

これにより、1つではなく2つの移行ファイルが生成されますが、どちらの移行も自動生成されます。

この手順はDjango 1.7+で機能します。

1
David Foster

私はDjango 1.7.7でこの状況に遭遇しました。私は私のために働いた次のことをすることになりました。

./manage.py makemigrations <app_name> --empty

データベースに影響を与えないmigrations.RenameFieldの単純なサブクラスを追加しました:

class RenameFieldKeepDatabaseColumn(migrations.RenameField):
def database_backwards(self, app_label, schema_editor, from_state, to_state):
    pass

def database_forwards(self, app_label, schema_editor, from_state, to_state):
    pass
1
alphanumeric0