Добавляем уникальное (unique) поле к существующей модели в Django

Добавление нового уникального поля к существующей модели Django (при условии, что оно не может быть пустым) не самая простая задача. В чем проблема? Представим, что мы создаем миграцию, в которой добавляем уникальное поле, скажем, uuid. Наша модель после изменений выглядит так:

import uuid
from django.db import models

class MyUUIDModel(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    # остальные поля

Создаем миграцию при помощи makemigrations и пробуем её применить. Что получается?

django.db.utils.IntegrityError: could not create unique index "myuuidmodel_id_key"
DETAIL:  Key (id)=(b0b6cc4f-be6e-49a1-a9d1-7b2e494324fd) is duplicated.

Функция uuid.uuid4, которую мы передаем в качестве конструктора значений нового поля, во время миграции вызывается только один раз, а у всех существующих объектов MyUUIDModel поле id в результате принимает одно и то же значение. В принципе, ничего сложного, решение лежит на поверхности: 

  1. Создаем новое поле, разрешаем ему быть пустым и принимать любые значения (null=True)
  2. Заполняем поле у всех записей сгенерированными разными значениями
  3. Запрещаем запись пустых значений и добавляем требование к уникальности (unique=True)

Именно такое решение и предлагают разработчики Django в официальной документации. Но можно поступить иначе. Правда, для этого придется подредактировать код миграции, созданной django автоматически, но зато в итоге мы получим желаемый результат за один проход migrate. По-сути, мы выполняем те-же самые действия, просто не разделяем их на разные миграции:

  1. Отредактируем метод migrations.AddField, убрав требование к уникальности и добавив null=True.
  2. Добавим функцию, которая заполнит поле id уникальными значениями у всех имеющиеся записей в БД, вызовем её после создания поля (migrations.AddField).
  3. Изменим поле при помощи migrations.AlterField, вернув ему unique=True и убрав null=True.
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models, migrations
import uuid

def generate_uuid(apps, schema_editor):
    Company = apps.get_model('myuuidmodel', 'MyUUIDModel')
    for obj in MyUUIDModel.objects.all().iterator():
        obj.id = uuid.uuid4()
        obj.save()

class Migration(migrations.Migration):

    dependencies = [
        ('main', '0001_initial'),
    ]

    operations = [
        migrations.AddField(
            model_name='myuuidmodel',
            name='id',
            field=models.UUIDField(editable=False, max_length=32, null=True, verbose_name='UUID'),
            preserve_default=False,
        ),
        migrations.RunPython(
            generate_uuid,
        ),
        migrations.AlterField(
            model_name='myuuidmodel',
            name='id',
            field=models.UUIDField(editable=False, max_length=32, unique=True, verbose_name='UUID'),
            preserve_default=True,
        )
    ]

Источник: https://code.djangoproject.com/ticket/23408