이 문서에서는 발생할 수 있는 다양한 시나리오에 대해 데이터베이스 마이그레이션을 구성하고 쓰는 방법을 설명합니다. 마이그레이션에 대한 소개 자료는 :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와 같은 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",
),
),
],
),
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 =”을 제거하고 마이그레이션을 생성해야 합니다.
5월 04, 2024