Django / Southを使用してモデルの名前を変更する最も簡単な方法は?


141

私はSouthのサイト、Google、SOでこれに対する答えを探していましたが、これを行う簡単な方法を見つけることができませんでした。

Southを使用してDjangoモデルの名前を変更したい。次のものがあるとします。

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

class FooTwo(models.Model):
    name = models.CharField()
    foo = models.ForeignKey(Foo)

そして、あなたはFooをBarに変換したい、すなわち

class Bar(models.Model):
    name = models.CharField()

class FooTwo(models.Model):
    name = models.CharField()
    foo = models.ForeignKey(Bar)

簡単にするために、名前をからFooに変更しようとしていますが、今のところメンバーをBar無視します。fooFooTwo

サウスを使用してこれを行う最も簡単な方法は何ですか?

  1. 私はおそらくデータの移行を行うことができましたが、それはかなり複雑に思われます。
  2. たとえばdb.rename_table('city_citystate', 'geo_citystate')、カスタム移行を記述しますが、この場合、外部キーを修正する方法がわかりません。
  3. あなたが知っているより簡単な方法は?

5
参照してくださいstackoverflow.com/questions/3235995/...名前の変更のためのモデルフィールドではなく、モデルを
メカニカルカタツムリ

ジャンゴ> = 1.8のために最適化されたソリューションstackoverflow.com/questions/25091130/...
化学プログラマー

回答:


130

最初の質問に答えるために、単純なモデル/テーブルの名前変更は非常に簡単です。次のコマンドを実行します。

./manage.py schemamigration yourapp rename_foo_to_bar --empty

(更新2:--auto代わりに、--empty以下の警告を回避するようにしてください。ヒントについては@KFBに感謝します。)

Southの古いバージョンを使用している場合は、のstartmigration代わりに必要になりschemamigrationます。

次に、移行ファイルを次のように手動で編集します。

class Migration(SchemaMigration):

    def forwards(self, orm):
        db.rename_table('yourapp_foo', 'yourapp_bar')


    def backwards(self, orm):
        db.rename_table('yourapp_bar','yourapp_foo')   

db_tableモデルクラスのMetaオプションを使用すると、これをより簡単に行うことができます。ただし、そのたびに、コードベースのレガシーウェイトが増加します。クラス名がテーブル名と異なると、コードの理解と維持が難しくなります。わかりやすくするために、このような単純なリファクタリングを完全にサポートしています。

(更新)本番環境でこれを試したところ、移行を適用しようとすると奇妙な警告が表示されました。と言いました:

The following content types are stale and need to be deleted:

    yourapp | foo

Any objects related to these content types by a foreign key will also
be deleted. Are you sure you want to delete these content types?
If you're unsure, answer 'no'.

私は「いいえ」と答え、すべてが元気であるように見えました。


3
Leopdが--emptyではなく--autoを使用してスキーマの移行を作成することで、エラーメッセージを回避できました。次に、移行ファイルを編集して、テーブルの削除/作成をdb.rename_table()呼び出しに変更しました。これは非常にうまく機能しているようです。
KFB

4
エラーが発生することなく、2011年9月2日にこのテクニックを使用しました。たぶん、Southの新しいバージョンがエラーの問題を解決したのかもしれません。
Chip Tol

1
これを更新してくれてありがとう!以下のJianの回答は、「send_create_signal」の呼び出しを維持することが重要であると述べていますが、それについて何か知っていますか?同意する場合は、サンプルの移行を更新してください。
mrooney

5
これはそのテーブルのインデックスの名前を変更しないことに注意してください。将来、古いテーブルと同じ名前で新しいテーブルを作成すると、インデックス名の衝突によりエラーが発生する可能性があります。この手法を使用していましたが、今後は明示的に新しいテーブルを作成し、データを移行してから、古いテーブルを削除します。
ジェレミーバンクス

3
元のモデルへのM2Mテーブルなど、自動生成されたテーブルの列名も、この方法では移行されません。
spookylukey 2013年

66

で変更を加えてmodels.pyから実行します

./manage.py schemamigration --auto myapp

移行ファイルを調べると、テーブルを削除して新しいテーブルを作成していることがわかります

class Migration(SchemaMigration):

    def forwards(self, orm):
        # Deleting model 'Foo'                                                                                                                      
        db.delete_table('myapp_foo')

        # Adding model 'Bar'                                                                                                                        
        db.create_table('myapp_bar', (
        ...
        ))
        db.send_create_signal('myapp', ['Bar'])

    def backwards(self, orm):
        ...

これはあなたが望むものではありません。代わりに、次のように移行を編集します。

class Migration(SchemaMigration):

    def forwards(self, orm):
        # Renaming model from 'Foo' to 'Bar'                                                                                                                      
        db.rename_table('myapp_foo', 'myapp_bar')                                                                                                                        
        if not db.dry_run:
            orm['contenttypes.contenttype'].objects.filter(
                app_label='myapp', model='foo').update(model='bar')

    def backwards(self, orm):
        # Renaming model from 'Bar' to 'Foo'                                                                                                                      
        db.rename_table('myapp_bar', 'myapp_foo')                                                                                                                        
        if not db.dry_run:
            orm['contenttypes.contenttype'].objects.filter(app_label='myapp', model='bar').update(model='foo')

updateステートメントがない場合、db.send_create_signal呼び出しはContentType新しいモデル名で新しいを作成します。ただし、データベースオブジェクトがそれを指している場合(たとえばを介して)に備えて、すでに持っているものよりも優れてupdateContentTypeますGenericForeignKey

また、名前を変更したモデルの外部キーであるいくつかの列の名前を変更した場合は、忘れずに

db.rename_column(myapp_model, foo_id, bar_id)

2
KeyError:「アプリ 'contenttypes'のモデル 'contenttype'は、この移行では利用できません。」というエラーが表示されます。また、私はdjango_content_typeテーブルを持っていますが、contenttypesテーブルは持っていません。(ジャンゴ1.6)
セス

2
@Sethこれを回避するには、個別のデータ移行でContentTypeモデルを更新contenttypes.ContentTypeし、への--frozenフラグを使用して凍結モデルにモデルを追加しました./manage.py datamigration。例:./manage.py datamigration --frozen contenttypes myapp update_contenttypes。次に、上で指定したコンテンツタイプ更新コードでmyapp_migrations / NNNN_update_contenttypes.pyを編集します。
Geoffrey Hing

@GeoffreyHingパラメータはフリーズされていると思います。south.readthedocs.io/en/latest/ormfreezing.htmlしかし、あなたの助けを本当にありがとう、それは本当に役に立ちました。
ccsakuweb

5

サウスはそれ自体を行うことはできません-それがかつてBar何をFoo使っていたかをどのように知るのですか?これは、カスタム移行を作成するためのものです。ForeignKey上記で行ったようにコードを変更することができ、適切なフィールドとテーブルの名前を変更するだけで、好きなように変更できます。

最後に、本当にこれを行う必要がありますか?モデルの名前を変更する必要はまだありません-モデル名は実装の詳細にすぎません-特にverbose_nameメタオプションの可用性を考えると。


7
または、コードでモデルの名前を変更しますが、db_tableメタオプションを使用してデータベーステーブル名を同じにします。
Daniel Roseman、

@Daniel-がdb_table外部キー名の導出に使用されているかどうか知っていますか?
ドミニクロジャー

私は信じています。モデル名を変更してdb_tableを設定しても、すべてが期待どおりに機能するはずです。
Davor Lucic

1
@DanielRosemanこれはスレッド全体で最高のソリューションです!
joerick 14

-1

上記のLeopdのソリューションに従いました。ただし、モデル名は変更されませんでした。コードで手動で変更しました(これもFKと呼ばれる関連モデルで)。そして、もう1つのサウスマイグレーションを実行しましたが、-fakeオプションを使用しています。これにより、モデル名とテーブル名が同じになります。

気がついたのは、最初にモデル名を変更してから、マイグレーションファイルを編集してから適用することです。ずっときれい。

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