backend/django

Django: 기존에 있는 값에 unique 필드 추가하기

seul chan 2019. 2. 4. 05:57

기존에 값이 있는 db에 unique한 필드를 추가하고 migration을 하려고 하면 다음과 같은 내용이 나온다.

Please select a fix:
 1) Provide a one-off default now (will be set on all existing rows with a null value for this column)
 2) Quit, and let me add a default in models.py
Select an option: 1
Please enter the default value now, as valid Python
The datetime and django.utils.timezone modules are available, so you can do e.g. timezone.now
Type 'exit' to exit this prompt
>>> 

아무 값이나 추가하여도 migration은 잘 되지만 migrate시에 기존 값의 unique=True가 충돌이 나서 migrate이 진행되지 않는다.

그동안은 꼼수로

null=True로 migrate => shell에 들어가서 값 unique하게 변경 => 다시 unique=True로 migarte

하는 과정을 거쳐서 진행하였는데, 장고 문서를 보던 중 migartion 파일에서 Runpython을 활용해서 이를 해결할 수 있는 방법을 발견하여 사용해보았다.

장고 문서는 여기에서 확인이 가능하다.

1. 일단 unique=True로 migration을 한다.

나는 ref_id를 추가하는 마이그레이션 파일을 만들었다.

0002_xxxx 파일이 만들어진 것을 확인할 수 있다.

# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2019-02-03 20:41
from __future__ import unicode_literals

from django.db import migrations, models


class Migration(migrations.Migration):

    dependencies = [
        ('invests', '0001_xxxxxx'),
    ]

    operations = [
        migrations.AddField(
            model_name='invest',
            name='ref_id',
            field=models.CharField(default='', max_length=255, unique=True),
        ),
    ]

2. 해당 migration 파일에서 unique=True를 null=True로 변경한다.

Runpython을 진행하기 전 해당 필드를 생성하기 위함이다. (그냥 null=True로 migration 한 이후에 나중에 model을 변경해도 무방하다.)

... # 위와 동일
    operations = [
        migrations.AddField(
            model_name='invest',
            name='ref_id',
            field=models.CharField(default='', max_length=255, null=True),
        ),
    ]

3. 새로운 migartaion 파일을 생성하여 Runpython script를 넣어준다.

나는 0003_add_ref_id라는 파일을 만들었다.

# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2019-02-03 20:41
from __future__ import unicode_literals

from django.db import migrations, models


def get_tid(apps, schema_editor):
    Mymodel = apps.get_model('app_name', 'MyModel')
    for row in MyModel.objects.all():
        # 여기에서 넣고싶은 값을 추가해준다. 나는 해당 모델이 fk로 가진 user id와 인스턴스의 id를 ref_id로 추가하였다.
        row.ref_id = '{}-{}'.format(row.user.id, row.id)
        row.save(update_fields=['ref_id'])


class Migration(migrations.Migration):

    dependencies = [
        # 위에서 생성된 migration 파일의 이름을 추가해준다.
        ('invests', '0002_xxxxxx'),
    ]

    operations = [
        migrations.RunPython(get_tid, reverse_code=migrations.RunPython.noop),
    ]

4. migrate

migration 파일을 만들고 (unique=True인 migration 파일이 새로 생성된다) migrate을 하면 정상적으로 migrate가 됨을 볼 수 있다.

# 새 migaration 파일 생성 (0004_xxxxx)
python manage.py makemigrations 

# migrate
python manage.py migrate