장고 IntegrityError 해결: 고유값 필드 생성하기
상황
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가 필요하다. 데이터가 이미 고유하게 업데이트되었기 때문에, 유니크 제약을 추가하는 데 문제가 없어진다.
둘 중 어떤 방법을 선택하든, 중복 문제는 자연스럽게 해결된다.