summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrei Kulakov <andrei.avk@gmail.com>2015-02-04 11:29:08 -0500
committerLoic Bistuer <loic.bistuer@gmail.com>2015-02-20 21:53:15 +0700
commit564487601e34b9962ad16bc7e4e62c8b68928c84 (patch)
treebe1eb0d7a6ed76b05c5260992427faba65f355d0
parent18dceab05bda23933d1c3782738a4095fa3b3af8 (diff)
[1.8.x] Fixed #23932 -- Added how-to on migrating unique fields.
Backport of 1f9e44030e9c5300b97ef7b029f482c53a66f13b from master
-rw-r--r--docs/howto/writing-migrations.txt89
1 files changed, 89 insertions, 0 deletions
diff --git a/docs/howto/writing-migrations.txt b/docs/howto/writing-migrations.txt
index 7edea84329..59a636626a 100644
--- a/docs/howto/writing-migrations.txt
+++ b/docs/howto/writing-migrations.txt
@@ -67,3 +67,92 @@ Then, to leverage this in your migrations, do the following::
operations = [
migrations.RunPython(forwards, hints={'target_db': 'default'}),
]
+
+Migrations that add unique fields
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Applying a "plain" migration that adds a unique non-nullable field to a table
+with existing rows will raise an error because the value used to populate
+existing rows is generated only once, thus breaking the unique constraint.
+
+Therefore, the following steps should be taken. In this example, we'll add a
+non-nullable :class:`~django.db.models.UUIDField` with a default value. Modify
+the respective field according to your needs.
+
+* Add the field on your model with ``default=...`` and ``unique=True``
+ arguments. In the example, we use ``uuid.uuid4`` for the default.
+
+* Run the :djadmin:`makemigrations` command.
+
+* Edit the created migration file.
+
+ The generated migration class should look similar to this::
+
+ class Migration(migrations.Migration):
+
+ dependencies = [
+ ('myapp', '0003_auto_20150129_1705'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='mymodel',
+ name='uuid',
+ field=models.UUIDField(max_length=32, unique=True, default=uuid.uuid4),
+ ),
+ ]
+
+ You will need to make three changes:
+
+ * Add a second :class:`~django.db.migrations.operations.AddField` operation
+ copied from the generated one and change it to
+ :class:`~django.db.migrations.operations.AlterField`.
+
+ * On the first operation (``AddField``), change ``unique=True`` to
+ ``null=True`` -- this will create the intermediary null field.
+
+ * Between the two operations, add a
+ :class:`~django.db.migrations.operations.RunPython` or
+ :class:`~django.db.migrations.operations.RunSQL` operation to generate a
+ unique value (UUID in the example) for each existing row.
+
+ The resulting migration should look similar to this::
+
+ # -*- coding: utf-8 -*-
+ from __future__ import unicode_literals
+
+ from django.db import migrations, models
+ import uuid
+
+ def gen_uuid(apps, schema_editor):
+ MyModel = apps.get_model('myapp', 'MyModel')
+ for row in MyModel.objects.all():
+ row.uuid = uuid.uuid4()
+ row.save()
+
+ class Migration(migrations.Migration):
+
+ dependencies = [
+ ('myapp', '0003_auto_20150129_1705'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='mymodel',
+ name='uuid',
+ field=models.UUIDField(default=uuid.uuid4, null=True),
+ ),
+ # omit reverse_code=... if you don't want the migration to be reversible.
+ migrations.RunPython(gen_uuid, reverse_code=migrations.RunPython.noop),
+ migrations.AlterField(
+ model_name='mymodel',
+ name='uuid',
+ field=models.UUIDField(default=uuid.uuid4, unique=True),
+ ),
+ ]
+
+* Now you can apply the migration as usual with the :djadmin:`migrate` command.
+
+ Note there is a race condition if you allow objects to be created while this
+ migration is running. Objects created after the ``AddField`` and before
+ ``RunPython`` will have their original ``uuid``’s overwritten.