2015-02-03 18:09:54 +00:00
|
|
|
|
===========================
|
|
|
|
|
Writing database migrations
|
|
|
|
|
===========================
|
|
|
|
|
|
|
|
|
|
This document explains how to structure and write database migrations for
|
|
|
|
|
different scenarios you might encounter. For introductory material on
|
|
|
|
|
migrations, see :doc:`the topic guide </topics/migrations>`.
|
|
|
|
|
|
|
|
|
|
.. _data-migrations-and-multiple-databases:
|
|
|
|
|
|
|
|
|
|
Data migrations and multiple databases
|
|
|
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
|
|
|
|
|
|
When using multiple databases, you may need to figure out whether or not to
|
|
|
|
|
run a migration against a particular database. For example, you may want to
|
|
|
|
|
**only** run a migration on a particular database.
|
|
|
|
|
|
|
|
|
|
In order to do that you can check the database connection's alias inside a
|
|
|
|
|
``RunPython`` operation by looking at the ``schema_editor.connection.alias``
|
|
|
|
|
attribute::
|
|
|
|
|
|
|
|
|
|
from django.db import migrations
|
|
|
|
|
|
|
|
|
|
def forwards(apps, schema_editor):
|
|
|
|
|
if not schema_editor.connection.alias == 'default':
|
|
|
|
|
return
|
|
|
|
|
# Your migration code goes here
|
|
|
|
|
|
|
|
|
|
class Migration(migrations.Migration):
|
|
|
|
|
|
|
|
|
|
dependencies = [
|
|
|
|
|
# Dependencies to other migrations
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
operations = [
|
|
|
|
|
migrations.RunPython(forwards),
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
.. versionadded:: 1.8
|
|
|
|
|
|
|
|
|
|
You can also provide hints that will be passed to the :meth:`allow_migrate()`
|
|
|
|
|
method of database routers as ``**hints``:
|
|
|
|
|
|
|
|
|
|
.. snippet::
|
|
|
|
|
:filename: myapp/dbrouters.py
|
|
|
|
|
|
|
|
|
|
class MyRouter(object):
|
|
|
|
|
|
2015-02-19 07:27:58 +00:00
|
|
|
|
def allow_migrate(self, db, app_label, model_name=None, **hints):
|
2015-02-03 18:09:54 +00:00
|
|
|
|
if 'target_db' in hints:
|
|
|
|
|
return db == hints['target_db']
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
Then, to leverage this in your migrations, do the following::
|
|
|
|
|
|
|
|
|
|
from django.db import migrations
|
|
|
|
|
|
|
|
|
|
def forwards(apps, schema_editor):
|
|
|
|
|
# Your migration code goes here
|
|
|
|
|
|
|
|
|
|
class Migration(migrations.Migration):
|
|
|
|
|
|
|
|
|
|
dependencies = [
|
|
|
|
|
# Dependencies to other migrations
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
operations = [
|
|
|
|
|
migrations.RunPython(forwards, hints={'target_db': 'default'}),
|
|
|
|
|
]
|
2015-02-04 16:29:08 +00:00
|
|
|
|
|
2015-02-19 07:27:58 +00:00
|
|
|
|
If your ``RunPython`` or ``RunSQL`` operation only affects one model, it's good
|
|
|
|
|
practice to pass ``model_name`` as a hint to make it as transparent as possible
|
|
|
|
|
to the router. This is especially important for reusable and third-party apps.
|
|
|
|
|
|
2015-02-04 16:29:08 +00:00
|
|
|
|
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.
|