# Django 6.0.7 문서

# 데이터베이스 마이그레이션을 생성하는 방법

이 문서에서는 발생할 수 있는 다양한 시나리오에 대해 데이터베이스 마이그레이션을 구성하고 쓰는 방법을 설명합니다. 마이그레이션에 대한 소개 자료는 :doc: ‘주제 가이드’를 참조하십시오.

## 데이터 마이그레이션 및 여러 데이터베이스를 사용합니다.

여러 데이터베이스를 사용할 때 특정 데이터베이스에 대해 마이그레이션을 실행할지 여부를 결정해야 할 수도 있습니다. 예를 들어 특정 데이터베이스에서 **만* 마이그레이션을 실행할 수 있습니다.

그러기 위해서 당신은 ``schema_editor.connection”을 보면 ``RunPython” 운영 내에서 데이터베이스 연결의 별칭을 확인할 수 있습니다.별칭 속성:입니다.

    from django.db import migrations

    def forwards(apps, schema_editor):
        if schema_editor.connection.alias != "default":
            return
        # Your migration code goes here

    class Migration(migrations.Migration):
        dependencies = [
            # Dependencies to other migrations
        ]

        operations = [
            migrations.RunPython(forwards),
        ]

**hints``로 힌트를 제공할 수도 있으며, 이 힌트는 다음과 같이 데이터베이스 라우터의 :meth:`allow_migrate 메서드로 전달됩니다.

`myapp/dbrouters.py`

    class MyRouter:
        def allow_migrate(self, db, app_label, model_name=None, **hints):
            if "target_db" in hints:
                return db == hints["target_db"]
            return True

마이그레이션 시 이 기능을 활용하려면 다음을 수행하십시오.

    from django.db import migrations

    def forwards(apps, schema_editor):
        # Your migration code goes here
        ...

    class Migration(migrations.Migration):
        dependencies = [
            # Dependencies to other migrations
        ]

        operations = [
            migrations.RunPython(forwards, hints={"target_db": "default"}),
        ]

만일 당신의 ``RunPython” 또는 “RunSQL” 작업이 하나의 모델에만 영향을 준다면, 가능한 한 투명하게 라우터에 알리기 위해 “model_name”을 힌트로 전달하는 것이 좋은 관행입니다. 이는 재사용 가능한 앱과 타사 앱에서 특히 중요합니다.

## 고유한 필드를 추가하는 마이그레이션입니다.

기존 행이 있는 테이블에 null이 아닌 고유한 필드를 추가하는 “일반” 마이그레이션을 적용하면 기존 행을 채우는 데 사용되는 값이 한 번만 생성되므로 고유한 제약 조건이 해제되기 때문에 오류가 발생합니다.

따라서 다음 단계를 수행해야 합니다. 이 예에서는 Null이 아닌 :class를 추가합니다.’~django.db.db.class입니다.기본값인 UUIDField’입니다. 필요에 따라 각 필드를 수정합니다.

- ``default = uuid”를 사용하여 모델에 필드를 추가합니다.uuid4와 “hydp*”로 구성됩니다.True’ 인수(추가하려는 필드 유형에 적합한 기본값 선택)를 선택합니다.

- :djadmin:’make migration’ 명령을 실행합니다. 이는 ``Add Field” 작전으로 이주를 창출해야 합니다.

- 동일한 앱에 대해 “마이 앱으로 마이그레이션 – 비우기”를 두 번 실행하여 두 개의 빈 마이그레이션 파일을 생성합니다. 아래의 예에서 의미 있는 이름을 지정하기 위해 마이그레이션 파일의 이름을 변경했습니다.

- “AddField” 작업을 자동 생성 마이그레이션(새 파일 3개 중 첫 번째 파일)에서 마지막 마이그레이션으로 복사하고 “AddField”를 “AlterField”로 변경하고 “uuid” 및 “model” 수입을 추가합니다. 예를 들어 다음과 같습니다.

  `0006_remove_uuid_null.py`

      # Generated by Django A.B on YYYY-MM-DD HH:MM
      from django.db import migrations, models
      import uuid

      class Migration(migrations.Migration):
          dependencies = [
              ("myapp", "0005_populate_uuid_values"),
          ]

          operations = [
              migrations.AlterField(
                  model_name="mymodel",
                  name="uuid",
                  field=models.UUIDField(default=uuid.uuid4, unique=True),
              ),
          ]

- 첫 번째 마이그레이션 파일을 편집합니다. 생성된 마이그레이션 클래스는 다음과 유사하게 표시되어야 합니다.

  `0004_add_uuid_field.py`

      class Migration(migrations.Migration):
          dependencies = [
              ("myapp", "0003_auto_20150129_1705"),
          ]

          operations = [
              migrations.AddField(
                  model_name="mymodel",
                  name="uuid",
                  field=models.UUIDField(default=uuid.uuid4, unique=True),
              ),
          ]

  ‘’변신’’을 하세요.True’ to “null ull True” – 이렇게 하면 중간 null 필드가 생성되고 모든 행에 고유 값을 채울 때까지 고유한 제약 조건 생성이 지연됩니다.

- 첫 번째 빈 마이그레이션 파일에서 :class를 추가하십시오.’~django.db.migrations.operations입니다.Python’ 또는 :class를 실행합니다.’~django.db.migrations.operations입니다.SQL 작업을 실행하여 각 기존 행에 대해 고유한 값(예제의 UUID)을 생성합니다. 또한 “uuid”의 가져오기를 추가합니다. 예를 들어 다음과 같습니다.

  `0005_populate_uuid_values.py`

      # Generated by Django A.B on YYYY-MM-DD HH:MM
      from django.db import migrations
      import uuid

      def gen_uuid(apps, schema_editor):
          MyModel = apps.get_model("myapp", "MyModel")
          for row in MyModel.objects.all():
              row.uuid = uuid.uuid4()
              row.save(update_fields=["uuid"])

      class Migration(migrations.Migration):
          dependencies = [
              ("myapp", "0004_add_uuid_field"),
          ]

          operations = [
              # omit reverse_code=... if you don't want the migration to be reversible.
              migrations.RunPython(gen_uuid, reverse_code=migrations.RunPython.noop),
          ]

- 이제 :djadmin:’migrate’ 명령을 사용하여 평소와 같이 마이그레이션을 적용할 수 있습니다.

  마이그레이션이 실행되는 동안 개체를 만들 수 있는 경우 경합 조건이 있습니다. “AddField” 이후와 “Run Python” 이전에 만들어진 물체들은 원래의 “uuid”를 덮어쓸 것입니다.

### 비원자성 마이그레이션입니다.

DDL 트랜잭션을 지원하는 데이터베이스(SQLite 및 Postgre)입니다.SQL) 마이그레이션은 기본적으로 트랜잭션 내에서 실행됩니다. 대형 테이블에서 데이터 마이그레이션을 수행하는 경우와 같은 사용 사례의 경우 “원자” 속성을 “거짓”으로 설정하여 트랜잭션에서 마이그레이션이 실행되지 않도록 할 수 있습니다.

    from django.db import migrations

    class Migration(migrations.Migration):
        atomic = False

이러한 마이그레이션에서는 모든 작업이 트랜잭션 없이 실행됩니다. :func:`~django.db.transaction.atomic`을 사용하거나 ``RunPython``에 ``atomic=True``를 전달하여 마이그레이션의 일부 작업을 트랜잭션 안에서 실행할 수 있습니다.

다음은 더 작은 배치로 큰 테이블을 업데이트하는 비원자 데이터 마이그레이션의 예입니다.

    import uuid

    from django.db import migrations, transaction

    def gen_uuid(apps, schema_editor):
        MyModel = apps.get_model("myapp", "MyModel")
        while MyModel.objects.filter(uuid__isnull=True).exists():
            with transaction.atomic():
                for row in MyModel.objects.filter(uuid__isnull=True)[:1000]:
                    row.uuid = uuid.uuid4()
                    row.save()

    class Migration(migrations.Migration):
        atomic = False

        operations = [
            migrations.RunPython(gen_uuid),
        ]

“원자” 특성은 DDL 트랜잭션을 지원하지 않는 데이터베이스(예: MySQL, Oracle)에는 영향을 미치지 않습니다. (MySQL의 ‘dsl statement support https://dev.mysql.com/doc/refman/en/atomic-ddl.html’_은(는) 롤백할 수 있는 트랜잭션에 포함된 여러 개의 문이 아닌 개별 문을 말합니다.)

## 마이그레이션 순서 제어

Django는 각 마이그레이션의 파일 이름이 아니라 “이민” 클래스의 ``dependency”와 “run_before”라는 두 가지 속성을 사용하여 마이그레이션이 적용되어야 하는 순서를 결정합니다.

“djadmin:”make migration” 명령을 사용했다면 자동 생성된 마이그레이션은 생성 과정의 일부로 정의되기 때문에 “dependency”가 이미 실행 중인 것을 보았을 것입니다.

“의존성” 재산은 다음과 같이 선언됩니다.

    from django.db import migrations

    class Migration(migrations.Migration):
        dependencies = [
            ("myapp", "0123_the_previous_migration"),
        ]

일반적으로 이 정도면 충분하지만 때때로 마이그레이션이 다른 마이그레이션 전에 실행되도록 해야 할 수도 있습니다. 예를 들어, 이 기능은 타사 앱의 마이그레이션을 다음 이후에 실행하도록 하는 데 유용합니다. 설정:’AUTH_USER_MODEL을 대체합니다.

이를 위해 귀사에 의존해야 할 모든 마이그레이션을 귀국의 ``이민” 등급의 ``실행 전” 속성으로 배치하십시오.

    class Migration(migrations.Migration):
        ...

        run_before = [
            ("third_party_app", "0001_do_awesome"),
        ]

가능하면 ``실행 전”보다 “의존적”을 사용하는 것을 선호합니다. 작성 중인 마이그레이션 이후에 실행할 마이그레이션에 ``의존성”을 지정하는 것이 바람직하지 않거나 비실용적인 경우에만 “run_before”를 사용해야 합니다.

## 타사 앱 간에 데이터를 마이그레이션하고 있습니다.

데이터 마이그레이션을 사용하여 한 타사 응용 프로그램에서 다른 응용 프로그램으로 데이터를 이동할 수 있습니다.

나중에 기존 앱을 제거할 계획이라면 기존 앱의 설치 여부에 따라 “의존성” 속성을 설정해야 합니다. 그렇지 않으면 이전 앱을 제거한 후 종속성이 누락됩니다. 마찬가지로 exc를 확인해야 합니다.기존 앱에서 모델을 검색하는 ``apps.get_model()” 통화에서 “Lookup Error”가 발생합니다. 이 방법을 사용하면 먼저 이전 앱을 설치한 다음 제거하지 않고도 프로젝트를 아무 곳에나 배포할 수 있습니다.

다음은 마이그레이션 예 입니다:

`myapp/migrations/0124_move_old_app_to_new_app.py`

    from django.apps import apps as global_apps
    from django.db import migrations

    def forwards(apps, schema_editor):
        try:
            OldModel = apps.get_model("old_app", "OldModel")
        except LookupError:
            # The old app isn't installed.
            return

        NewModel = apps.get_model("new_app", "NewModel")
        NewModel.objects.bulk_create(
            NewModel(new_attribute=old_object.old_attribute)
            for old_object in OldModel.objects.all()
        )

    class Migration(migrations.Migration):
        operations = [
            migrations.RunPython(forwards, migrations.RunPython.noop),
        ]
        dependencies = [
            ("myapp", "0123_the_previous_migration"),
            ("new_app", "0001_initial"),
        ]

        if global_apps.is_installed("old_app"):
            dependencies.append(("old_app", "0001_initial"))

또한 마이그레이션이 적용되지 않을 때 수행할 작업도 고려하십시오. 위의 예와 같이 아무 작업도 수행하지 않거나 새 응용 프로그램에서 일부 또는 모든 데이터를 제거할 수 있습니다. :mod의 두 번째 인수를 조정합니다.’~django.db.migrations.operations입니다.이에 따라 파이썬을 실행합니다.

## `ManyToManyField` 를 바꾸어 `through` 모델을 사용합니다.

`ManyToManyField`를 ``through`` 모델을 사용하도록 변경하면, 기본 마이그레이션이 기존 테이블을 삭제하고 새 테이블을 생성하여 기존 관계가 손실됩니다. 이를 방지하려면 `SeparateDatabaseAndState`를 사용하여 기존 테이블 이름을 새 테이블 이름으로 변경하면서 동시에 마이그레이션 자동 감지기에는 새 모델이 생성되었음을 알릴 수 있습니다. 기존 테이블 이름과 제약 조건 이름은 :djadmin:`sqlmigrate` 또는 `dbshell`을 통해 확인할 수 있습니다. 새 테이블 이름은 through 모델의 ``_meta.db_table`` 속성을 통해 확인할 수 있습니다. 새로운 `through` 모델은 Django와 같은 `ForeignKey`s 이름을 사용해야 합니다. 또한 추가 필드가 필요한 경우 `SeparateDatabaseAndState` 이후의 작업에서 추가해야 합니다.

예를 들어 우리가 ``다수로”를 ``저자”로 연결한 ``책” 모델을 가지고 있다면 다음과 같이 새로운 분야의 “is_primary”를 가진 ``저자책”을 추가할 수 있을 것입니다.

    from django.db import migrations, models
    import django.db.models.deletion

    class Migration(migrations.Migration):
        dependencies = [
            ("core", "0001_initial"),
        ]

        operations = [
            migrations.SeparateDatabaseAndState(
                database_operations=[
                    # Old table name from checking with sqlmigrate, new table
                    # name from AuthorBook._meta.db_table.
                    migrations.RunSQL(
                        sql="ALTER TABLE core_book_authors RENAME TO core_authorbook",
                        reverse_sql="ALTER TABLE core_authorbook RENAME TO core_book_authors",
                    ),
                ],
                state_operations=[
                    migrations.CreateModel(
                        name="AuthorBook",
                        fields=[
                            (
                                "id",
                                models.AutoField(
                                    auto_created=True,
                                    primary_key=True,
                                    serialize=False,
                                    verbose_name="ID",
                                ),
                            ),
                            (
                                "author",
                                models.ForeignKey(
                                    on_delete=django.db.models.deletion.DO_NOTHING,
                                    to="core.Author",
                                ),
                            ),
                            (
                                "book",
                                models.ForeignKey(
                                    on_delete=django.db.models.deletion.DO_NOTHING,
                                    to="core.Book",
                                ),
                            ),
                        ],
                        options={
                            "constraints": [
                                models.UniqueConstraint(
                                    fields=["author", "book"],
                                    name="unique_author_book",
                                )
                            ],
                        },
                    ),
                    migrations.AlterField(
                        model_name="book",
                        name="authors",
                        field=models.ManyToManyField(
                            to="core.Author",
                            through="core.AuthorBook",
                        ),
                    ),
                ],
            ),
            migrations.AddField(
                model_name="authorbook",
                name="is_primary",
                field=models.BooleanField(default=False),
            ),
        ]

## 관리되지 않는 모델을 관리 대상으로 변경합니다.

관리되지 않는 모델(:attr:’managed >False <django.db.db.closed)을 변경하려는 경우입니다.관리 대상인 Options.managed>’에서는 “Meta.managed” 변경 작업을 포함하는 마이그레이션에 표시되는 스키마 변경 사항이 적용되지 않을 수 있으므로 모델을 변경하기 전에 “Managed =”을 제거하고 마이그레이션을 생성해야 합니다.