장고 IntegrityError 해결: 고유값 필드 생성하기

소요 시간: 3분

상황

Django 모델에서 CustomUser에 identifier라는 필드를 추가하려고 했는데, 기본값을 설정하지 않아서 makemigrations할 때 이런 에러가 발생:

It is impossible to add a non-nullable field 'identifier' to customuser without specifying a default

그걸 해결하기 위해 기본값을 설정하고 다시 마이그레이션을 했더니, 이번엔 다시 이런 에러가 터졌다:

django.db.utils.IntegrityError: (1062, "Duplicate entry 'identifier_default_value' for key 'accounts_customuser.identifier'")

이 에러는 내가 지정한 기본값이 중복되는 문제에서 비롯됐다. identifier 필드는 고유해야 하는데, 기본값이 중복되니까 문제 발생. 어떻게 해결할 수 있을까?


해결책

1. 각 사용자에게 고유한 기본값을 자동으로 생성하기

2. unique=True`를 나중에 추가하기


방법 1: 사용자별 고유 기본값을 자동 생성

고유값을 자동으로 할당하려면, 기본값을 UUID나 이메일 등으로 지정하면 된다. 이 방식은 간단하고 명확하다.

import uuid
from django.db import models

class CustomUser(models.Model):
    identifier = models.CharField(
        max_length=50,
        unique=True,
        default=uuid.uuid4,  # UUID 자동 생성
    )

이렇게 설정하면, 각 사용자마다 고유한 UUID 값이 자동으로 할당된다. `uuid.uuid4()`는 유니크한 값을 보장하니까 중복 문제는 자연스럽게 해결된다. 진짜 간단하다.

이제 마이그레이션을 실행해서 데이터베이스에 반영한다:

python manage.py makemigrations
python manage.py migrate

이렇게 하면, 기존 데이터에 대해서도 각 사용자마다 고유한 `identifier` 값이 자동으로 채워진다.


방법 2: unique=True는 나중에 추가하기

만약 identifier 필드에 unique=True 제약을 넣어야 한다면, 처음에는 unique=True 없이 마이그레이션을 하고, 그 후에 데이터를 고유하게 업데이트한 뒤 unique=True를 추가하는 방식으로 해결할 수 있다.

처음에는 unique=True를 제외하고 기본값을 설정한다. 예를 들어, 임시 기본값으로 'temporary_default_value'를 넣고 시작한다.

identifier = models.CharField(max_length=50, default='temporary_default_value')

이렇게 하면, 기존에 있던 모든 사용자에게 임시 기본값이 할당된다.

그 후에 temporary_default_value를 고유한 값(예: UUID)으로 업데이트하는 스크립트를 작성한다. 이때 각 사용자에게 고유한 identifier를 부여한다.

from django.db import migrations
import uuid

def populate_unique_identifiers(apps, schema_editor):
    CustomUser = apps.get_model('accounts', 'CustomUser')
    for user in CustomUser.objects.all():
        user.identifier = str(uuid.uuid4())  # UUID로 고유값 할당
        user.save()

class Migration(migrations.Migration):
    dependencies = [
        # 이전 마이그레이션 참조
    ]
    operations = [
        migrations.RunPython(populate_unique_identifiers),
    ]

이 마이그레이션을 실행하면, 기존에 임시 기본값이 할당된 모든 사용자에게 유니크한 identifier가 부여된다.

마지막으로, identifier 필드에 unique=True를 추가하는 마이그레이션을 만든다. 이제 고유값이 잘 할당되었으니, 이 필드에 유니크 제약을 추가할 수 있다.

identifier = models.CharField(max_length=50, unique=True)

이 마이그레이션을 적용하면, `identifier` 필드가 고유한 값을 가지고 `unique=True` 제약도 정상적으로 적용된다.


결론

방법 1이 가장 간단하다. 기본값을 자동으로 생성해주는 UUID를 사용하는 게 가장 직관적이고, 마이그레이션을 복잡하게 만들지 않는다.

하지만, 나중에 unique=True 제약을 추가하고 싶다면 방법 2가 필요하다. 데이터가 이미 고유하게 업데이트되었기 때문에, 유니크 제약을 추가하는 데 문제가 없어진다.

둘 중 어떤 방법을 선택하든, 중복 문제는 자연스럽게 해결된다.

장고 리스트