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


153

기존 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

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

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

감사!

답변:


127

내가 이것을 시도했을 때, 당신은 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먼저 시도한 다음 마이그레이션 파일을 확인하십시오.


답변 해주셔서 감사합니다. 그 후 내가 설명한 단계를 사용하여 마이그레이션했지만 기존 데이터 또는 빈 데이터베이스로 시도한 경우 궁금합니다.
Fiver

2
로컬 env의 sqlite에 몇 행만 있지만 기존 데이터로 시도했습니다 (생산으로 이동할 때 마이그레이션 파일을 포함하여 모든 것을 지우려고합니다)
wasabigeek

4
마이그레이션 파일에서 모델 이름을 사용하는 경우 모델 이름을 변경할 필요가 없습니다 apps.get_model. 그것을 알아 내기 위해 많은 시간이 걸렸습니다.
ahmed

9
django 2.0에서는 모델 이름을 변경하면 모델 이름을 바꿀지 ./manage.py makemigrations myapp묻는 메시지가 표시됩니다. 예 : myapp.Foo 모델의 이름을 Bar로 바꿨나요? [y / N] 'y'로 대답하면 마이그레이션 migration.RenameModel('Foo', 'Bar')에 이름이 바뀐 필드에 대한 동일한 개수 가 포함 됩니다. :-)
ceasaro

1
manage.py makemigrations myapp"실패 할 수 있습니다."모델 이름과 해당 필드를 한 번에 변경하면 수동으로 추가해야 할 수도 있습니다. 자동 감지 기능을 사용하면 이전 이름의 모델을 삭제하고 새 모델을 추가 한 것처럼 보입니다. 다른 이름으로 만들면 이전 테이블의 데이터가 손실됩니다. " Django 2.1 Docs 저에게 빈 마이그레이션을 만들고 모델 이름을 바꾸고 makemigrations평소대로 실행하는 것으로 충분했습니다 .
hlongmore

37

처음에는 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에서 작동합니다.


3
감사합니다! 매우 도움이되었습니다. 그러나 참고-Foo를 Bar로 이름을 바꾼 후 Bar라는 새 모델을 만들었으므로 PostgreSQL 필드 인덱스의 이름을 수동으로 바꾸거나 PostgreSQL 필드 인덱스를 제거해야했습니다.
Anatoly Scherbakov

감사합니다! 핵심 부분은 모델 내외부의 모든 외래 키 이름을로 변환하는 것으로 생각합니다 IntegerField. 이것은 나를 위해 완벽하게 작동했으며 올바른 이름으로 다시 만들 수 있다는 이점이 있습니다. 당연히 모든 마이그레이션을 실제로 실행하기 전에 검토하는 것이 좋습니다!
zelanix

감사합니다! 다른 모델에 외래 키가있는 모델의 이름을 바꾸기 위해 많은 다른 전략을 시도했지만 (1 ~ 3 단계) 이것이 유일한 방법이었습니다.
MSH

변경 ForeignKey에들 IntegerField의 것은 오늘 내 하루를 저장!
mehmet

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/


공유해 주셔서 감사합니다. 답변이 도움이 되었다면 wasibigeek +1해야합니다.
Fiver

7

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

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

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


고마워요! Django 1.11에도 완벽 함
Francisco

6

또한 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 에서이 접근법을 시도했습니다.


5

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

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


고마워 친구, "아니오"를 친 후 아무 일도 일어나지 않아 혼란스러워
farhawa

3

불행히도 데이터베이스에 이전 테이블 이름을 남기는 이름 바꾸기 마이그레이션과 관련된 문제 (각 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

추가 정리 :

  • 이주에 대한 스쿼시

장고의 버그 :


3

ceasaro 의견을 확인하고 추가하고 싶었습니다. Django 2.0은 이제 자동 으로이 작업을 수행하는 것 같습니다.

Django 2.2.1에 있습니다 makemigrations. 모형의 이름을 바꾸고 실행하는 작업을 수행해야했습니다 .

저는 여기에서 특정 클래스의 이름을 변경 한 경우는 요청 AB, 내가 예와 만났 마이그레이션을 선택하고 모든 일에 보인다.

참고 프로젝트 / 마이그레이션 폴더 내의 파일에서 이전 모델 이름을 바꾸지 않았습니다.


1

두 개의 테이블 이름을 바꿔야했습니다. 그러나 Django는 하나의 모델 이름 변경 만 발견했습니다. 장고는 추가 된 다음 제거 된 모델을 반복하기 때문에 발생했습니다. 각 쌍에 대해 동일한 앱인지, 동일한 필드인지 확인합니다 . 하나의 테이블에만 이름을 바꿀 테이블에 대한 외래 키가 없었습니다 (기억 한 것처럼 외래 키에는 모델 클래스 이름이 포함됨). 즉, 하나의 테이블 만 필드를 변경하지 않았습니다. 그것이 눈에 띄는 이유입니다.

그래서, 솔루션에 모델 클래스의 이름을 변경, 한 번에 하나의 테이블의 이름을 변경하는 것입니다 models.py가능성, views.py및 마이그레이션을. 그런 다음 코드에서 다른 참조 (모델 클래스 이름, 관련 (쿼리) 이름, 변수 이름)를 검사하십시오. 필요한 경우 마이그레이션을 수행하십시오. 그런 다음 선택적으로 이러한 모든 마이그레이션을 하나로 통합하십시오 (가져 오기도 복사해야 함).


1

나는이 답변 에 대한 그의 의견에 @ceasaro 단어를 만들 입니다.

최신 버전의 Django는 변경 사항을 감지하고 수행 한 작업에 대해 묻습니다. 또한 Django가 일부 마이그레이션 명령의 실행 순서를 혼합 할 수 있다고 덧붙였습니다.

그것은 작은 변경 사항을 적용하고 실행하는 것이 현명 makemigrations하고 migrate에러가 발생하면 마이그레이션 파일을 편집 할 수 있습니다.

오류를 피하기 위해 일부 라인 실행 순서를 변경할 수 있습니다.


모델 이름을 변경하고 외래 키 등이 정의 된 경우에는 작동하지 않습니다.
Dean Kayton

이전 주석에서 확장 : 내가 수행하는 모든 작업이 모델 이름을 변경하고 makemigrations를 실행하는 경우 외래 키 등에서 'NameError : name'<oldmodel> 'is not defined'가 표시됩니다. 변경하고 makemigrations를 실행하면 가져 오기 오류가 발생합니다. admin.py에서 ...이 문제를 해결하고 makemigrations를 다시 실행하면 '<app.oldmodel> 모델의 이름을 <newmodel>으로 바 꾸었습니까?'라는 메시지가 표시되지만 마이그레이션을 적용하면 'ValueError : 필드 <app .newmodel.field1>은 '<app.oldmodel>'에 대한 게으른 참조로 선언되었지만 '<app>'앱은 '<oldmodel>'모델 등을 제공하지 않습니다 ... '
Dean Kayton

이 오류는 히스토리 마이그레이션에서 참조의 이름을 바꾸어야하는 것처럼 보입니다.
mhatch

@DeanKayton가 migrations.SeparateDatabaseAndState도움 이 될 것이라고 말할 것 입니까?
diogosimao

1

PyCharm과 같은 좋은 IDE를 사용하는 경우 모델 이름을 마우스 오른쪽 버튼으로 클릭하고 리팩터링-> 이름 바꾸기를 수행 할 수 있습니다. 이렇게하면 모델을 참조하는 모든 코드를 처리하는 데 따르는 어려움을 덜 수 있습니다. 그런 다음 makemigrations를 실행하고 마이그레이션하십시오. Django 2+는 단순히 이름 변경을 확인합니다.


-10

Django를 버전 10에서 버전 11로 업그레이드했습니다.

sudo pip install -U Django

( -U"업그레이드"의 경우) 문제를 해결했습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.