Programing

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

lottogame 2020. 6. 30. 08:25
반응형

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


기존 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먼저 시도한 다음 마이그레이션 파일을 확인하십시오.


처음에는 마이그레이션이 4 단계까지 제대로 작동했기 때문에 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에서 작동합니다.


나는 똑같은 일을해야했다. 모델을 한 번에 변경했습니다 (예 : 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 단계에서는 테이블 이름을 변경하고 싶다는 것을 확인해야했습니다. 마이그레이션하면 문제없이 테이블 이름이 변경되었습니다.

Then I changed the name of the ForeignKey field to match, and again was asked by Makemigrations to confirm that I wanted to change the name. Migrate than made the change.

So I took this in two steps without any special file editing. I did get errors at first because I forgot to change the admin.py file, as mentioned by @wasibigeek.


I also faced the problem as v.thorey described and found that his approach is very useful but can be condensed into fewer steps which are actually step 5 to 8 as Fiver described without step 1 to 4 except that step 7 needs to be changed as my below step 3. The overall steps are as follow:

Step 1: Edit the related field names in 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()

Step 2: Create an empty migration

python manage.py makemigrations --empty myapp

Step 3: Edit the Migration class in the migration file created in Step 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')
]

Step 4: Apply the migration

python manage.py migrate

Done

P.S. I've tried this approach on Django 1.9


I am using Django version 1.9.4

I have follow the following steps:-

I have just rename the model oldName to NewName Run python manage.py makemigrations. It will ask you for Did you rename the appname.oldName model to NewName? [y/N] select Y

Run python manage.py migrate and it will ask you for

The following content types are stale and need to be deleted:

appname | oldName
appname | NewName

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

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

It rename and migrate all existing data to new named table for me.


Unfortunately, I found problems (each django 1.x) with rename migration which leave old table names in the database.

Django doesn't even try anything on the old table, just rename his own model. The same problem with foreign keys, and indices in general - changes there are not tracked properly by Django.

The simplest solution (workaround):

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

The real solution (an easy way to switch all indices, constraints, triggers, names, etc in 2 commits, but rather for smaller tables):

commit A:

  1. create the same model as the old one
# deprecated - TODO: TO BE REMOVED
class Foo(model.Model):
    ...

class Bar(model.Model):
    ...
  1. switch code to work with new model Bar only. (including all relations on the schema)

In migration prepare RunPython, which copy data from Foo to Bar (including id of Foo)

  1. optional optimization (if needed for greater tables)

commit B: (no rush, do it when an entire team is migrated)

  1. safe drop of the old model Foo

further cleanup:

  • squash on migrations

bug in Django:


I needed to rename a couple of tables. But only one model rename was noticed by Django. That happened because Django iterates over added, then removed models. For each pair it checks if they're of the same app and have identical fields. Only one table had no foreign keys to tables to be renamed (foreign keys contain model class name, as you remember). In other words, only one table had no field changes. That's why it was noticed.

So, the solution is to rename one table at a time, changing model class name in models.py, possibly views.py, and making a migration. After that inspect your code for other references (model class names, related (query) names, variable names). Make a migration, if needed. Then, optionally combine all these migrations into one (make sure to copy imports as well).


I would make @ceasaro words, mine on his comment on this answer.

Newer versions of Django can detect changes and ask about what was done. I also would add that Django might mix the order of execution of some migration commands.

It would be wise to apply small changes and run makemigrations and migrate and if the error occurs the migration file can be edited.

Some lines order of execution can be changed to avoid erros.


Just wanted to confirm and add upon ceasaro comment. Django 2.0 seems to do this automatically now.

I'm on Jango 2.2.1, all I had to do what to rename the model and run makemigrations.

Here it asks if I had renamed the specific class from A to B, I chose yes and ran migrate and all seems to work.

Note I did not rename the old model name in any files inside the project/migrations folder.


If you are using a good IDE like PyCharm you can right click on the model name and do a refactor -> rename. This saves you the trouble of going through all your code that references the model. Then run makemigrations and migrate. Django 2+ will simply confirm name change.


I upgraded Django from version 10 to version 11:

sudo pip install -U Django

(-U for "upgrade") and it solved the problem.

참고URL : https://stackoverflow.com/questions/25091130/django-migration-strategy-for-renaming-a-model-and-relationship-fields

반응형