4 つのモデルを含む django アプリがあります。これらのモデルの 1 つを別のアプリにする必要があることに気付きました。移行のためにサウスをインストールしましたが、これは自動的に処理できるものではないと思います。モデルの 1 つを古いアプリから新しいアプリに移行するにはどうすればよいですか?
また、本番システムなどを移行できるように、これを繰り返し可能なプロセスにする必要があることに注意してください。
4 つのモデルを含む django アプリがあります。これらのモデルの 1 つを別のアプリにする必要があることに気付きました。移行のためにサウスをインストールしましたが、これは自動的に処理できるものではないと思います。モデルの 1 つを古いアプリから新しいアプリに移行するにはどうすればよいですか?
また、本番システムなどを移行できるように、これを繰り返し可能なプロセスにする必要があることに注意してください。
共通アプリと特定アプリの 2 つのアプリがあるとします。
myproject/
|-- common
| |-- migrations
| | |-- 0001_initial.py
| | `-- 0002_create_cat.py
| `-- models.py
`-- specific
|-- migrations
| |-- 0001_initial.py
| `-- 0002_create_dog.py
`-- models.py
次に、モデル common.models.cat を特定のアプリ (正確には specific.models.cat) に移動します。最初にソース コードを変更してから、次を実行します。
$ python manage.py schemamigration specific create_cat --auto
+ Added model 'specific.cat'
$ python manage.py schemamigration common drop_cat --auto
- Deleted model 'common.cat'
myproject/
|-- common
| |-- migrations
| | |-- 0001_initial.py
| | |-- 0002_create_cat.py
| | `-- 0003_drop_cat.py
| `-- models.py
`-- specific
|-- migrations
| |-- 0001_initial.py
| |-- 0002_create_dog.py
| `-- 0003_create_cat.py
`-- models.py
次に、両方の移行ファイルを編集する必要があります。
#0003_create_cat: replace existing forward and backward code
#to use just one sentence:
def forwards(self, orm):
db.rename_table('common_cat', 'specific_cat')
if not db.dry_run:
# For permissions to work properly after migrating
orm['contenttypes.contenttype'].objects.filter(
app_label='common',
model='cat',
).update(app_label='specific')
def backwards(self, orm):
db.rename_table('specific_cat', 'common_cat')
if not db.dry_run:
# For permissions to work properly after migrating
orm['contenttypes.contenttype'].objects.filter(
app_label='specific',
model='cat',
).update(app_label='common')
#0003_drop_cat:replace existing forward and backward code
#to use just one sentence; add dependency:
depends_on = (
('specific', '0003_create_cat'),
)
def forwards(self, orm):
pass
def backwards(self, orm):
pass
これで、両方のアプリの移行が変更を認識し、生活が少し楽になりました:-) 移行間のこの関係を設定することが成功の鍵です。今あなたがするならば:
python manage.py migrate common
> specific: 0003_create_cat
> common: 0003_drop_cat
両方の移行を行い、
python manage.py migrate specific 0002_create_dog
< common: 0003_drop_cat
< specific: 0003_create_cat
物事を下に移行します。
スキーマのアップグレードには共通アプリを使用し、ダウングレードには特定のアプリを使用したことに注意してください。これは、ここでの依存関係がどのように機能するかによるものです。
Potr Czachurのanswerに基づいて構築するには、ForeignKeys が関係する状況はより複雑であり、少し異なる方法で処理する必要があります。
(次の例は、現在の回答で参照されているcommon
およびspecific
アプリに基づいています)。
# common/models.py
class Cat(models.Model):
# ...
class Toy(models.Model):
belongs_to = models.ForeignKey(Cat)
# ...
その後、に変更されます
# common/models.py
from specific.models import Cat
class Toy(models.Model):
belongs_to = models.ForeignKey(Cat)
# ...
# specific/models.py
class Cat(models.Model):
# ...
ランニング
./manage.py schemamigration common --auto
./manage.py schemamigration specific --auto # or --initial
次の移行が生成されます (Django ContentType の変更を意図的に無視しています。それを処理する方法については、以前に参照された回答を参照してください)。
# common/migrations/0009_auto__del_cat.py
class Migration(SchemaMigration):
def forwards(self, orm):
db.delete_table('common_cat')
db.alter_column('common_toy', 'belongs_to_id', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['specific.Cat']))
def backwards(self, orm):
db.create_table('common_cat', (
# ...
))
db.alter_column('common_toy', 'belongs_to_id', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['common.Cat']))
# specific/migrations/0004_auto__add_cat.py
class Migration(SchemaMigration):
def forwards(self, orm):
db.create_table('specific_cat', (
# ...
))
def backwards(self, orm):
db.delete_table('specific_cat')
ご覧のとおり、新しいテーブルを参照するように FK を変更する必要があります。移行が適用される順序を知るために依存関係を追加する必要があります (したがって、テーブルに FK を追加しようとする前にテーブルが存在することになります) 。依存関係は逆方向に適用されます。
# common/migrations/0009_auto__del_cat.py
class Migration(SchemaMigration):
depends_on = (
('specific', '0004_auto__add_cat'),
)
def forwards(self, orm):
db.alter_column('common_toy', 'belongs_to_id', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['specific.Cat']))
def backwards(self, orm):
db.rename_table('specific_cat', 'common_cat')
db.alter_column('common_toy', 'belongs_to_id', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['common.Cat']))
# specific/migrations/0004_auto__add_cat.py
class Migration(SchemaMigration):
def forwards(self, orm):
db.rename_table('common_cat', 'specific_cat')
def backwards(self, orm):
pass
South のドキュメントによると、順方向に移行する場合depends_on
は確実に0004_auto__add_cat
前に実行されます が、逆方向に移行する場合は逆の順序で実行されます。ロールバックのままにすると、テーブル参照テーブルが存在しないため、ForeignKey を移行しようとしたときにロールバックが失敗します。0009_auto__del_cat
db.rename_table('specific_cat', 'common_cat')
specific
common
うまくいけば、これは既存のソリューションよりも「現実世界」の状況に近く、誰かがこれを役立つと思うでしょう. 乾杯!
モデルはアプリとあまり緊密に結合されていないため、移行は非常に簡単です。Django はデータベース テーブルの名前にアプリ名を使用するため、アプリを移動したい場合は、SQLALTER TABLE
ステートメントを介してデータベース テーブルの名前を変更するか、さらに簡単に、モデルのクラスでdb_table
パラメーターMeta
を使用して、古い名前。
これまでコード内の任意の場所で ContentTypes またはジェネリック リレーションを使用していた場合はapp_label
、既存のリレーションが保持されるように、移動中のモデルを指すコンテンツ タイプの名前を変更することをお勧めします。
もちろん、保存するデータがまったくない場合は、データベース テーブルを完全に削除してから./manage.py syncdb
再度実行するのが最も簡単です。
私がここに数回戻ってきて、それを正式なものにすることに決めたので、私が現在落ち着いているプロセス。
これはもともと Potr Czachurの回答 とMatt Briançonの回答に基づいて、South 0.8.4を使用して構築されました
# Caution: This finds OneToOneField and ForeignKey.
# I don't know if this finds all the ways of specifying ManyToManyField.
# Hopefully Django or South throw errors if you have a situation like that.
>>> Cat._meta.get_all_related_objects()
[<RelatedObject: common:toy related to cat>,
<RelatedObject: identity:microchip related to cat>]
したがって、この拡張されたケースでは、次のような別の関連モデルを発見しました。
# Inside the "identity" app...
class Microchip(models.Model):
# In reality we'd probably want a ForeignKey, but to show the OneToOneField
identifies = models.OneToOneField(Cat)
...
# Create the "new"-ly renamed model
# Yes I'm changing the model name in my refactoring too.
python manage.py schemamigration specific create_kittycat --auto
# Drop the old model
python manage.py schemamigration common drop_cat --auto
# Update downstream apps, so South thinks their ForeignKey(s) are correct.
# Can skip models like Toy if the app is already covered
python manage.py schemamigration identity update_microchip_fk --auto
チームメイトが更新されたアプリで移行を作成するなど、マージの競合が発生した場合に、より繰り返し可能なプロセスになります。
基本的create_kittycat
に、すべての現在の状態に依存し、その後はすべてに依存しcreate_kittycat
ます。
# create_kittycat
class Migration(SchemaMigration):
depends_on = (
# Original model location
('common', 'the_one_before_drop_cat'),
# Foreign keys to models not in original location
('identity', 'the_one_before_update_microchip_fk'),
)
...
# drop_cat
class Migration(SchemaMigration):
depends_on = (
('specific', 'create_kittycat'),
)
...
# update_microchip_fk
class Migration(SchemaMigration):
depends_on = (
('specific', 'create_kittycat'),
)
...
# create_kittycat
class Migration(SchemaMigration):
...
# Hopefully for create_kittycat you only need to change the following
# 4 strings to go forward cleanly... backwards will need a bit more work.
old_app = 'common'
old_model = 'cat'
new_app = 'specific'
new_model = 'kittycat'
# You may also wish to update the ContentType.name,
# personally, I don't know what its for and
# haven't seen any side effects from skipping it.
def forwards(self, orm):
db.rename_table(
'%s_%s' % (self.old_app, self.old_model),
'%s_%s' % (self.new_app, self.new_model),
)
if not db.dry_run:
# For permissions, GenericForeignKeys, etc to work properly after migrating.
orm['contenttypes.contenttype'].objects.filter(
app_label=self.old_app,
model=self.old_model,
).update(
app_label=self.new_app,
model=self.new_model,
)
# Going forwards, should be no problem just updating child foreign keys
# with the --auto in the other new South migrations
def backwards(self, orm):
db.rename_table(
'%s_%s' % (self.new_app, self.new_model),
'%s_%s' % (self.old_app, self.old_model),
)
if not db.dry_run:
# For permissions, GenericForeignKeys, etc to work properly after migrating.
orm['contenttypes.contenttype'].objects.filter(
app_label=self.new_app,
model=self.new_model,
).update(
app_label=self.old_app,
model=self.old_model,
)
# Going backwards, you probably should copy the ForeignKey
# db.alter_column() changes from the other new migrations in here
# so they run in the correct order.
#
# Test it! See Step 6 for more details if you need to go backwards.
db.alter_column('common_toy', 'belongs_to_id', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['common.Cat']))
db.alter_column('identity_microchip', 'identifies_id', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['common.Cat']))
# drop_cat
class Migration(SchemaMigration):
...
def forwards(self, orm):
# Remove the db.delete_table(), if you don't at Step 7 you'll likely get
# "django.db.utils.ProgrammingError: table "common_cat" does not exist"
# Leave existing db.alter_column() statements here
db.alter_column('common_toy', 'belongs_to_id', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['specific.KittyCat']))
def backwards(self, orm):
# Copy/paste the auto-generated db.alter_column()
# into the create_kittycat migration if you need backwards to work.
pass
# update_microchip_fk
class Migration(SchemaMigration):
...
def forwards(self, orm):
# Leave existing db.alter_column() statements here
db.alter_column('identity_microchip', 'identifies_id', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['specific.KittyCat']))
def backwards(self, orm):
# Copy/paste the auto-generated db.alter_column()
# into the create_kittycat migration if you need backwards to work.
pass
# the_one_before_create_kittycat
class Migration(SchemaMigration):
# You many also need to add more models to South's FakeORM if you run into
# more KeyErrors, the trade-off chosen was to make going forward as easy as
# possible, as that's what you'll probably want to do once in QA and once in
# production, rather than running the following many times:
#
# python manage.py migrate specific <the_one_before_create_kittycat>
models = {
...
# Copied from 'identity' app, 'update_microchip_fk' migration
u'identity.microchip': {
'Meta': {'object_name': 'Microchip'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'identifies': ('django.db.models.fields.related.OneToOneField', [], {to=orm['specific.KittyCat']})
},
...
}
python manage.py migrate
# If you need backwards to work
python manage.py migrate specific <the_one_before_create_kittycat>
Potr の優れた解決策に対するもう 1 つの修正があります。以下をspecific/0003_create_catに追加します
depends_on = (
('common', '0002_create_cat'),
)
この依存関係が設定されていない限り、South は、 specific/0003_create_catが実行common_cat
された時点でテーブルが存在することを保証せず、エラーをスローします。django.db.utils.OperationalError: no such table: common_cat
依存関係が明示的に設定されていない限り、South は辞書順で移行を実行します。common
since は、テーブルの名前を変更する前にspecific
すべての移行が実行される前に来るためcommon
、Potr が示した元の例ではおそらく再現されないでしょう。しかし、名前common
を toapp2
およびspecific
to に変更すると、app1
この問題が発生します。
したがって、上記の @Potr からの元の応答を使用しても、South 0.8.1 および Django 1.5.1 では機能しませんでした。他の人に役立つことを期待して、私にとって何がうまくいったかを以下に投稿しています。
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
db.rename_table('common_cat', 'specific_cat')
if not db.dry_run:
db.execute(
"update django_content_type set app_label = 'specific' where "
" app_label = 'common' and model = 'cat';")
def backwards(self, orm):
db.rename_table('specific_cat', 'common_cat')
db.execute(
"update django_content_type set app_label = 'common' where "
" app_label = 'specific' and model = 'cat';")