[python] 모델 및 관계 필드 이름 바꾸기를위한 장고 마이그레이션 전략

기존 Django 프로젝트에서 이름을 바꾸려는 모델과 외래 키 관계가있는 다른 모델이 많이있는 여러 모델의 이름을 바꿀 계획입니다. 이 작업을 여러 번 수행해야한다고 확신하지만 정확한 절차는 확실하지 않습니다.

Django 앱에서 다음 모델로 시작한다고 가정 해 봅시다 myapp.

class Foo(models.Model):
    name = models.CharField(unique=True, max_length=32)
    description = models.TextField(null=True, blank=True)


class AnotherModel(models.Model):
    foo = models.ForeignKey(Foo)
    is_awesome = models.BooleanField()


class YetAnotherModel(models.Model):
    foo = models.ForeignKey(Foo)
    is_ridonkulous = models.BooleanField()

Foo이름이 실제로 이해되지 않고 코드에서 혼동을 일으키고 Bar훨씬 명확한 이름을 만들 수 있기 때문에 모델의 이름 을 바꾸고 싶습니다 .

Django 개발 문서에서 읽은 내용에서 다음과 같은 마이그레이션 전략을 가정합니다.

1 단계

수정 models.py:

class Bar(models.Model):  # <-- changed model name
    name = models.CharField(unique=True, max_length=32)
    description = models.TextField(null=True, blank=True)


class AnotherModel(models.Model):
    foo = models.ForeignKey(Bar)  # <-- changed relation, but not field name
    is_awesome = models.BooleanField()


class YetAnotherModel(models.Model):
    foo = models.ForeignKey(Bar)  # <-- changed relation, but not field name
    is_ridonkulous = models.BooleanField()

AnotherModel필드 이름은 foo변경되지 않지만 관계는 Bar모델 로 업데이트됩니다 . 내 추론은 한 번에 너무 많이 변경해서는 안되며이 필드 이름을로 변경하면 bar해당 열의 데이터가 손실 될 위험이 있다는 것입니다.

2 단계

빈 마이그레이션을 만듭니다.

python manage.py makemigrations --empty myapp

3 단계

Migration2 단계에서 작성된 마이그레이션 파일에서 클래스를 편집하여 RenameModel조작을 조작 목록 에 추가하십시오 .

class Migration(migrations.Migration):

    dependencies = [
        ('myapp', '0001_initial'),
    ]

    operations = [
        migrations.RenameModel('Foo', 'Bar')
    ]

4 단계

마이그레이션을 적용하십시오.

python manage.py migrate

5 단계

다음에서 관련 필드 이름을 편집하십시오 models.py.

class Bar(models.Model):
    name = models.CharField(unique=True, max_length=32)
    description = models.TextField(null=True, blank=True)


class AnotherModel(models.Model):
    bar = models.ForeignKey(Bar)  # <-- changed field name
    is_awesome = models.BooleanField()


class YetAnotherModel(models.Model):
    bar = models.ForeignKey(Bar)  # <-- changed field name
    is_ridonkulous = models.BooleanField()

6 단계

다른 빈 마이그레이션을 작성하십시오.

python manage.py makemigrations --empty myapp

7 단계

Migration6 단계에서 작성된 마이그레이션 파일에서 클래스를 편집하여 RenameField관련 필드 이름에 대한 오퍼레이션을 오퍼레이션 목록에 추가하십시오.

class Migration(migrations.Migration):

    dependencies = [
        ('myapp', '0002_rename_fields'),  # <-- is this okay?
    ]

    operations = [
        migrations.RenameField('AnotherModel', 'foo', 'bar'),
        migrations.RenameField('YetAnotherModel', 'foo', 'bar')
    ]

8 단계

두 번째 마이그레이션을 적용하십시오.

python manage.py migrate

새 변수 이름을 반영하기 위해 나머지 코드 (보기, 양식 등)를 업데이트하는 것 외에도 기본적으로 새 마이그레이션 기능은 어떻게 작동합니까?

또한 이것은 많은 단계처럼 보입니다. 마이그레이션 작업을 어떤 방식으로 압축 할 수 있습니까?

감사!



답변

내가 이것을 시도했을 때, 당신은 3 단계에서 7 단계를 응축 할 수있는 것 같습니다.

class Migration(migrations.Migration):

    dependencies = [
        ('myapp', '0001_initial'),
    ]

    operations = [
        migrations.RenameModel('Foo', 'Bar'),
        migrations.RenameField('AnotherModel', 'foo', 'bar'),
        migrations.RenameField('YetAnotherModel', 'foo', 'bar')
    ]

가져온 이름 (예 : admin.py 및 심지어 이전 마이그레이션 파일 (!))을 업데이트하지 않으면 오류가 발생할 수 있습니다.

업데이트 : ceasaro가 언급 했듯이 최신 버전의 Django는 일반적으로 모델의 이름이 바뀌 었는지 감지하고 요청할 수 있습니다. manage.py makemigrations먼저 시도한 다음 마이그레이션 파일을 확인하십시오.


답변

처음에는 5 단계까지 마이그레이션이 제대로 작동했기 때문에 Fiver의 방법이 효과적이라고 생각했지만 ‘ForeignKeyField (Foo)’에서 ‘ForeignKeyField (Bar)’로의 암시 적 변경은 마이그레이션과 관련이 없습니다. 이것이 관계 필드의 이름을 바꾸고 싶을 때 마이그레이션이 실패한 이유입니다 (5-8 단계). 내 경우에는 내 ‘AnotherModel’및 ‘YetAnotherModel’이 다른 앱에서 발송되기 때문일 수 있습니다.

그래서 아래 단계를 수행하여 모델 및 관계 필드의 이름을 변경했습니다.

나는 이것 의 방법 과 특히 otranzer의 트릭을 적용했습니다.

Fiver처럼 myapp에 있다고 가정 해 봅시다 .

class Foo(models.Model):
    name = models.CharField(unique=True, max_length=32)
    description = models.TextField(null=True, blank=True)

그리고 myotherapp에서 :

class AnotherModel(models.Model):
    foo = models.ForeignKey(Foo)
    is_awesome = models.BooleanField()


class YetAnotherModel(models.Model):
    foo = models.ForeignKey(Foo)
    is_ridonkulous = models.BooleanField()

1 단계:

모든 OneToOneField (Foo) 또는 ForeignKeyField (Foo)를 IntegerField ()로 변환하십시오. (이것은 관련된 Foo 객체의 id를 정수 필드의 값으로 유지합니다).

class AnotherModel(models.Model):
    foo = models.IntegerField()
    is_awesome = models.BooleanField()

class YetAnotherModel(models.Model):
    foo = models.IntegerField()
    is_ridonkulous = models.BooleanField()

그때

python manage.py makemigrations

python manage.py migrate

2 단계 : (Fiver의 2-4 단계와 유사)

모델명 변경

class Bar(models.Model):  # <-- changed model name
    name = models.CharField(unique=True, max_length=32)
    description = models.TextField(null=True, blank=True)

빈 마이그레이션을 만듭니다.

python manage.py makemigrations --empty myapp

그런 다음 다음과 같이 편집하십시오.

class Migration(migrations.Migration):

    dependencies = [
        ('myapp', '0001_initial'),
    ]

    operations = [
        migrations.RenameModel('Foo', 'Bar')
    ]

결국

python manage.py migrate

3 단계 :

IntegerField ()를 새로운 Bar Model을 사용하여 이전 ForeignKeyField 또는 OneToOneField로 다시 변환하십시오. (이전 정수 필드는 id를 저장하고 있었으므로 장고는 그것을 이해하고 연결을 다시 설정합니다.

class AnotherModel(models.Model):
    foo = models.ForeignKey(Bar)
    is_awesome = models.BooleanField()

class YetAnotherModel(models.Model):
    foo = models.ForeignKey(Bar)
    is_ridonkulous = models.BooleanField()

그런 다음 수행하십시오.

python manage.py makemigrations 

매우 중요한 것은이 단계에서 모든 새로운 마이그레이션을 수정하고 RenameModel Foo-> Bar 마이그레이션에 대한 종속성을 추가해야합니다. 따라서 AnotherModel과 YetAnotherModel이 모두 myotherapp에있는 경우 myotherapp에서 작성된 마이그레이션은 다음과 같아야합니다.

class Migration(migrations.Migration):

    dependencies = [
        ('myapp', '00XX_the_migration_of_myapp_with_renamemodel_foo_bar'),
        ('myotherapp', '00xx_the_migration_of_myotherapp_with_integerfield'),
    ]

    operations = [
        migrations.AlterField(
            model_name='anothermodel',
            name='foo',
            field=models.ForeignKey(to='myapp.Bar'),
        ),
        migrations.AlterField(
            model_name='yetanothermodel',
            name='foo',
            field=models.ForeignKey(to='myapp.Bar')
        ),
    ]

그때

python manage.py migrate

4 단계 :

결국 필드 이름을 바꿀 수 있습니다

class AnotherModel(models.Model):
    bar = models.ForeignKey(Bar) <------- Renamed fields
    is_awesome = models.BooleanField()


class YetAnotherModel(models.Model):
    bar = models.ForeignKey(Bar) <------- Renamed fields
    is_ridonkulous = models.BooleanField()

그런 다음 자동 이름 변경을 수행하십시오.

python manage.py makemigrations

(장고는 실제로 모델 이름을 바꾸 었는지 묻습니다, 예)

python manage.py migrate

그리고 그게 다야!

이것은 장고 1.8에서 작동합니다.


답변

나는 똑같은 일을하고 따라야했다. 모델을 한 번에 변경했습니다 (Fiver의 답변에서 1 단계와 5 단계를 함께). 그런 다음 스키마 마이그레이션을 작성했지만 다음과 같이 편집했습니다.

class Migration(SchemaMigration):
    def forwards(self, orm):
        db.rename_table('Foo','Bar')

    def backwards(self, orm):
        db.rename_table('Bar','Foo')

이것은 완벽하게 작동했습니다. 기존의 모든 데이터가 나타 났고 다른 모든 테이블은 Bar fine을 참조했습니다.

여기에서 : https://hanmir.wordpress.com/2012/08/30/rename-model-django-south-migration/


답변

Django 1.10의 경우 Makemigrations를 실행 한 다음 앱으로 마이그레이션하여 두 가지 모델 클래스 이름 (ForeignKey 및 데이터 포함)을 변경했습니다. Makemigrations 단계에서는 테이블 이름을 변경하고 싶다는 것을 확인해야했습니다. 마이그레이션하면 문제없이 테이블 이름이 변경되었습니다.

그런 다음 ForeignKey 필드의 이름을 일치하도록 변경하고 Makemigrations에서 다시 이름을 변경하고 싶다는 확인을 요청했습니다. 변경 한 것보다 마이그레이션하십시오.

그래서 특별한 파일 편집없이 두 단계로 이것을 수행했습니다. @wasibigeek에서 언급했듯이 admin.py 파일을 변경하는 것을 잊었 기 때문에 처음에는 오류가 발생했습니다.


답변

또한 v.thorey가 설명하고 그의 접근 방식이 매우 유용하지만 더 적은 단계로 압축 될 수 있음을 발견했습니다. 아래의 3 단계입니다. 전체 단계는 다음과 같습니다.

1 단계 : models.py에서 관련 필드 이름 편집

class Bar(models.Model):
    name = models.CharField(unique=True, max_length=32)
    description = models.TextField(null=True, blank=True)


class AnotherModel(models.Model):
    bar = models.ForeignKey(Bar)  # <-- changed field name
    is_awesome = models.BooleanField()


class YetAnotherModel(models.Model):
    bar = models.ForeignKey(Bar)  # <-- changed field name
    is_ridonkulous = models.BooleanField()

2 단계 : 빈 마이그레이션 만들기

python manage.py makemigrations --empty myapp

3 단계 : 2 단계에서 만든 마이그레이션 파일에서 마이그레이션 클래스 편집

class Migration(migrations.Migration):

dependencies = [
    ('myapp', '0001_initial'),
]

operations = [
    migrations.AlterField(
        model_name='AnotherModel',
        name='foo',
        field=models.IntegerField(),
    ),
    migrations.AlterField(
        model_name='YetAnotherModel',
        name='foo',
        field=models.IntegerField(),
    ),
    migrations.RenameModel('Foo', 'Bar'),
    migrations.AlterField(
        model_name='AnotherModel',
        name='foo',
        field=models.ForeignKey(to='myapp.Bar'),
    ),
    migrations.AlterField(
        model_name='YetAnotherModel',
        name='foo',
        field=models.ForeignKey(to='myapp.Bar'),
    ),
    migrations.RenameField('AnotherModel', 'foo', 'bar'),
    migrations.RenameField('YetAnotherModel', 'foo', 'bar')
]

4 단계 : 마이그레이션 적용

python manage.py migrate

끝난

추신 : 나는 장고 1.9 에서이 접근법을 시도했습니다.


답변

Django 버전 1.9.4를 사용하고 있습니다

다음 단계를 수행했습니다.

oldName 모델의 이름을 NewName Run으로 변경했습니다 python manage.py makemigrations. Did you rename the appname.oldName model to NewName? [y/N]Y 를
선택하라는 메시지가 표시됩니다.

실행 python manage.py migrate하면 요청합니다

다음 컨텐츠 유형이 오래되어 삭제해야합니다.

appname | oldName
appname | NewName

외래 키로 이러한 콘텐츠 형식과 관련된 모든 개체도 삭제됩니다. 이러한 컨텐츠 유형을 삭제 하시겠습니까? 확실하지 않으면 ‘아니요’라고 대답하십시오.

Type 'yes' to continue, or 'no' to cancel: Select No

기존의 모든 데이터의 이름을 바꾸고 새 명명 된 테이블로 마이그레이션합니다.


답변

불행히도 데이터베이스에 이전 테이블 이름을 남기는 이름 바꾸기 마이그레이션과 관련된 문제 (각 django 1.x)를 발견했습니다.

Django는 이전 테이블에서 아무것도 시도하지 않고 자신의 모델 이름을 바꿉니다. 외래 키와 같은 문제와 일반적인 색인-장고가 변경 사항을 제대로 추적하지 못합니다.

가장 간단한 해결책 (해결 방법) :

class Foo(models.Model):
     name = models.CharField(unique=True, max_length=32)
     ...
Bar = Foo  # and use Bar only

실제 솔루션 (모든 인덱스, 제약 조건, 트리거, 이름 등을 2 개의 커밋으로 전환하는 작은 방법 이지만 작은 테이블의 경우) :

커밋 A :

  1. 이전 모델과 동일한 모델을 만듭니다
# deprecated - TODO: TO BE REMOVED
class Foo(model.Model):
    ...

class Bar(model.Model):
    ...
  1. 새 모델 Bar에서만 작동하도록 코드를 전환하십시오 . (스키마에 대한 모든 관계 포함)

RunPythonFoo에서 Bar ( idFoo 포함) 로 데이터를 복사하는 마이그레이션 준비

  1. 선택적 최적화 (더 큰 테이블에 필요한 경우)

커밋 B : (서두르지 말고, 전체 팀이 마이그레이션 될 때 수행)

  1. 이전 모델의 안전한 드롭 Foo

추가 정리 :

  • 이주에 대한 스쿼시

장고의 버그 :