models.py
class Post(models.Model):
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) # 추가
...
해당 코드와 같이 Post 모델에 author 칼럼을 추가하고 마이그레이션 생성 명령을 수행한다면 문제없이 동작할 거다.
게시글에서의 작성자는 default, null, blank 등등 지정 유효성 조건은 전부 맞지 않다. 게시글이 생성될 때 작성자가 등록되기 때문이다. 1번 선택지를 선택하고 일회성 기본값으로 1을 입력해 준다.
0002_post_author.py
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
("blog", "0001_initial"),
]
operations = [
migrations.AddField(
model_name="post",
name="author",
field=models.ForeignKey(
default=1,
on_delete=django.db.models.deletion.CASCADE,
to=settings.AUTH_USER_MODEL,
),
preserve_default=False,
),
]
ALTER TABLE "blog_post" ADD COLUMN "author_id" bigint DEFAULT 1 NOT NULL CONSTRAINT "blog_post_author_id_dd7a8485_fk_accounts_user_id" REFERENCES "accounts_user"("id") DEFERRABLE INITIALLY DEFERRED; SET CONSTRAINTS "blog_post_author_id_dd7a8485_fk_accounts_user_id" IMMEDIATE;
ALTER TABLE "blog_post" ALTER COLUMN "author_id" DROP DEFAULT;
CREATE INDEX "blog_post_author_id_dd7a8485" ON "blog_post" ("author_id");
COMMIT;
author 필드의 default 값으로 1가 할당되어 있다. 이는 Post 테이블에서 새로운 레코드가 등록될 때 author 필드는 외래키로 연결한 부모 테이블의 레코드 중 기본 값으로 사용할 pk 값이 1인 레코드가 있기를 기대하고 있기 때문에 해당 레코드가 등록되어있어야 함을 뜻한다.
만약 User 테이블의 레코드가 등록되어있지 않은 상태에서 마이그레이트 명령을 수행할 시 Post 테이블의 레코드가 하나도 없는 상태라면 상관없지만 이미 레코드가 존재하고 있을 경우 마이그레이트 명령 수행 시 위와 같은 외래키 제약조건 에러가 발생한다.
수동적으로 부모 테이블에 레코드를 생성하고 마이그레이션을 수행하면 되겠지만 매번 그런 작업을 하게 된다면 번거롭기 마련이다.
마이그레이션시 유저 자동생성
0002_post_author.py
# Generated by Django 4.2.7 on 2024-04-06 18:01
import string
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
from django.utils.crypto import get_random_string
def create_user_if_empty(apps, schema_editor):
User = apps.get_model(settings.AUTH_USER_MODEL)
is_existed = User.objects.filter(pk=1).exists()
if is_existed is False:
random_username = "auto-" + get_random_string(
length=8, allowed_chars=string.ascii_letters
)
user = User.objects.create_user(
username=random_username, password=None, is_active=False, pk=1
)
print("auto_generated_user (pk=1):", user)
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
("blog", "0001_initial"),
]
operations = [
migrations.RunPython(create_user_if_empty, migrations.RunPython.noop),
migrations.AddField(
model_name="post",
name="author",
field=models.ForeignKey(
default=1,
on_delete=django.db.models.deletion.CASCADE,
to=settings.AUTH_USER_MODEL,
),
preserve_default=False,
),
]
- User모델의 username은 unique 하기 때문에 겹치지 않게끔 8자리의 아스키코드 문자열로 생성해 준다.
- 로그인이 불가능하게 password는 None으로 설정해 주고 is_active도 False로 주어 계정도 비활성화시켜 준다.
- RunPython 구문은 외래키 제약 조건이 추가되기 전 선언해 주도록 한다.
마이그레이트 명령 수행 시 pk=1인 유저 레코드가 생성되면서 마이그레이션이 성공한 것을 확인 가능하다.
마이그레이션시 카테고리 자동생성
models.py
class Category(models.Model):
name = models.CharField(max_length=50)
class Post(models.Model):
category = models.ForeignKey(Category, on_delete=models.CASCADE) # 추가
...
두 번째 예시이다.
Post의 레코드가 이미 존재하는 상태에서 Category의 레코드가 없는 경우 외래키 제약조건 에러가 발생할 것을 예상할 수 있다.
0003_category_post_category.py
# Generated by Django 4.2.7 on 2024-04-06 18:34
from django.db import migrations, models
import django.db.models.deletion
def create_initial_category(apps, schema_editor):
Category = apps.get_model("blog", "Category")
is_existed = Category.objects.filter(pk=1).exists()
if is_existed is False:
Category.objects.create(name="initial", pk=1)
class Migration(migrations.Migration):
dependencies = [
("blog", "0002_post_author"),
]
operations = [
migrations.CreateModel(
name="Category",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("name", models.CharField(max_length=50)),
],
),
migrations.RunPython(create_initial_category, migrations.RunPython.noop),
migrations.AddField(
model_name="post",
name="category",
field=models.ForeignKey(
default=1,
on_delete=django.db.models.deletion.CASCADE,
to="blog.category",
),
preserve_default=False,
),
]
해당 코드를 적용하고 마이그레이트 명령 수행 시 마이그레이션이 성공하고 위와 같이 author_id, category_id에 pk=1인 레코드를 참조하는 것을 확인할 수 있다.
이렇듯 레코드가 존재하는 상황에서 외래키를 추가하여 마이그레이션을 해야 할 때 자동적으로 처리할 수 있게끔 진행할 수 있다.
'기술정리 > Django' 카테고리의 다른 글
Django ManyToManyField 커스텀 모델 이전하기 (0) | 2024.04.08 |
---|---|
Django slug 필드를 활용하여 검색하기 (0) | 2024.04.06 |
Django 효율적인 대량 데이터 마이그레이션 방법 (0) | 2024.04.05 |
Django Instagram Project 2 (0) | 2022.11.20 |
Django Instagram Project 1 (0) | 2022.11.14 |