web-dev-qa-db-ja.com

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

私は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メンバーは今のところ無視します。

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

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

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

./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'.

私は「いいえ」と答え、すべてがうまくいくように見えました。

130
Leopd

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を作成します。ただし、データベースオブジェクトがそれを指している場合(たとえば、updateを介して)既に持っているContentTypeGenericForeignKeyする方が良いでしょう。

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

db.rename_column(myapp_model, foo_id, bar_id)
66
Jian

サウスはそれ自体ができません-BarFooを使用していたことをどのように認識するのでしょうか?これは、私がカスタム移行を作成するようなものです。上記で行ったようにコードでForeignKeyを変更できます。これは、適切なフィールドとテーブルの名前を変更する場合にすぎません。これは任意の方法で実行できます。

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

5
Dominic Rodger