이 문서에서는 발생할 수 있는 다양한 시나리오에 대해 데이터베이스 마이그레이션을 구성하고 쓰는 방법을 설명합니다. 마이그레이션에 대한 소개 자료는 :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),
    ]
<div></div>
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.transaction.transaction.transaction’을 사용하거나 “transaction”을 전달하여 트랜잭션 내 마이그레이션의 일부를 실행할 수 있습니다.”피톤을 운영하라”는 말은 사실입니다.
다음은 더 작은 배치로 큰 테이블을 업데이트하는 비원자 데이터 마이그레이션의 예입니다.
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  모델을 사용합니다.¶:class:~django.db.models.ManyToManyField를 through 모델을 사용하도록 변경하면, 기본 마이그레이션이 기존 테이블을 삭제하고 새 테이블을 생성하여 기존 관계를 잃게 됩니다. 이를 방지하려면, 마이그레이션 자동 감지기에 새 모델이 생성되었음을 알리면서 기존 테이블의 이름을 새 테이블 이름으로 변경하기 위해 SeparateDatabaseAndState`를 사용할 수 있습니다. 기존 테이블 이름은 :djadmin:`sqlmigrate 또는 dbshell`을 통해 확인할 수 있습니다. 새 테이블 이름은 모델의 ``_meta.db_table` 속성을 통해 확인할 수 있습니다. 새로운 through 모델은 Django와 같은 ForeignKeys 이름을 사용해야 합니다. 또한 추가 필드가 필요한 경우 작업에서 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",
                            ),
                        ),
                    ],
                ),
                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 =”을 제거하고 마이그레이션을 생성해야 합니다.
8월 06, 2024