하나의 django 앱에서 새로운 모델을 새로운 앱으로 마이그레이션하려면 어떻게해야합니까?

StackOverflow https://stackoverflow.com/questions/1258130

  •  12-09-2019
  •  | 
  •  

문제

네 가지 모델이있는 Django 앱이 있습니다. 이제 이러한 모델 중 하나가 별도의 앱에 있어야한다는 것을 알고 있습니다. 나는 마이그레이션을 위해 남쪽을 설치했지만 이것이 자동으로 처리 할 수있는 것이라고 생각하지 않습니다. 기존 앱에서 모델 중 하나를 새로운 모델로 어떻게 마이그레이션 할 수 있습니까?

또한, 나는 이것을 반복 가능한 프로세스가 되려면 생산 시스템 등을 마이그레이션 할 수 있도록해야한다는 것을 명심하십시오.

도움이 되었습니까?

해결책

남쪽을 사용하여 마이그레이션하는 방법.

두 가지 앱이 있다고 가정 해 봅시다 : 공통 및 구체적인 :

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을 특정 앱으로 이동하려고합니다 (정확하게 특정 .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'에스 대답, 외국 키와 관련된 상황은 더 복잡하고 약간 다르게 처리해야합니다.

(다음 예제는 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

남부 문서, depends_on 그것을 보장 할 것입니다 0004_auto__add_cat 전에 실행됩니다 0009_auto__del_cat 앞으로 이동할 때 그러나 거꾸로 마이그레이션 할 때 반대 순서. 우리가 떠난 경우 db.rename_table('specific_cat', 'common_cat') 에서 specific 롤백, the common 테이블 참조 테이블이 존재하지 않기 때문에 외국 키를 마이그레이션하려고 할 때 롤백이 실패합니다.

바라건대 이것은 기존 솔루션보다 "실제 세계"상황에 더 가깝고 누군가가 도움이 될 것입니다. 건배!

모델이 앱과 밀접하게 결합되지 않으므로 이동은 상당히 간단합니다. django는 데이터베이스 테이블의 이름으로 앱 이름을 사용하므로 앱을 이동하려면 SQL을 통해 데이터베이스 테이블의 이름을 바꿀 수 있습니다. ALTER TABLE 진술, 또는 심지어 간단하게 - 만 사용하십시오. db_table 매개 변수 당신의 모델에서 Meta 이전 이름을 언급하는 클래스.

지금까지 코드의 어느 곳에서나 ContentTypes 또는 일반 관계를 사용한 경우 app_label 기존 관계가 보존되도록 이동중인 모델을 가리키는 내용 유형의.

물론 보존 할 데이터가 전혀 없다면 가장 쉬운 일은 데이터베이스 테이블을 완전히 삭제하고 실행하는 것입니다. ./manage.py syncdb 다시.

다음은 POTR의 훌륭한 솔루션에 대한 또 하나의 수정 사항이 있습니다. 다음을 추가하십시오 특정/0003_create_cat

depends_on = (
    ('common', '0002_create_cat'),
)

이 의존성이 남쪽으로 설정되지 않는 한 common_cat 당시에 테이블이 존재합니다 특정/0003_create_cat 달리기, 던지고 있습니다 django.db.utils.OperationalError: no such table: common_cat 당신에게 오류.

사우스는 마이그레이션을 실행합니다 사전 문서 순서 의존성이 명시 적으로 설정되지 않는 한. 부터 common 전에 온다 specific 모든 common'의 마이그레이션은 테이블 이름을 바꾸기 전에 실행되므로 POTR이 표시 한 원래 예에서는 재현되지 않을 것입니다. 그러나 이름을 바꾸면 common 에게 app2 그리고 specific 에게 app1 이 문제가 발생합니다.

내가 여기로 몇 번 돌아 왔고 공식화하기로 결정한 이후로 내가 현재 해결 한 과정.

이것은 원래 구축되었습니다Potr Czachur의 대답그리고 Matt Briançon의 대답남쪽 0.8.4 사용

1 단계. 아동 외국의 주요 관계를 발견하십시오

# 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)

    ...

단계 2. 마이그레이션을 만듭니다

# 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

단계 3. 소스 제어 : 지금까지 변경 사항을 커밋합니다.

팀 동료가 업데이트 된 앱에서 마이그레이션을 작성하는 것과 같은 합병 충돌을 일으키는 경우보다 반복 가능한 프로세스입니다.

단계 4. 마이그레이션 간의 종속성을 추가합니다.

원래 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'),
    )
    ...

단계 5. 테이블의 이름 변경 변경 사항.

# 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

6 단계 6. 작업을 위해 뒤로 ()가 필요한 경우에만 Keyerror를 거꾸로 실행하십시오.

# 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']})
        },
        ...
    }

단계 7. 테스트 - 나에게 효과가있는 것은 당신의 실제 상황에 충분하지 않을 수 있습니다 :)

python manage.py migrate

# If you need backwards to work
python manage.py migrate specific <the_one_before_create_kittycat>

따라서 위의 @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';")

나는 Daniel Roseman이 그의 대답에서 제안한 것 중 하나의 더 명확한 버전을 줄 것입니다 ...

당신이 그냥 바꾸면 db_table 기존 테이블 이름을 가리 키도록 이동 한 모델의 메타 속성 (새 이름 대신 Django가 삭제하고했을 때 제공합니다. syncdb) 그러면 복잡한 남쪽 이동을 피할 수 있습니다. 예 :

원래의:

# app1/models.py
class MyModel(models.Model):
    ...

이동 후 :

# app2/models.py
class MyModel(models.Model):
    class Meta:
        db_table = "app1_mymodel"

이제 업데이트하려면 데이터 마이그레이션을 수행하면됩니다. app_label ~을 위한 MyModel 에서 django_content_type 테이블과 당신은 가기에 좋을 것입니다 ...

운영 ./manage.py datamigration django update_content_type 그런 다음 South가 생성 한 파일을 편집합니다.

def forwards(self, orm):
    moved = orm.ContentType.objects.get(app_label='app1', model='mymodel')
    moved.app_label = 'app2'
    moved.save()

def backwards(self, orm):
    moved = orm.ContentType.objects.get(app_label='app2', model='mymodel')
    moved.app_label = 'app1'
    moved.save()
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top