mirror of https://github.com/django/django.git
Fixed #24630 -- Clarified docs about RunPython transactions.
Thanks Markus Holtermann for review.
This commit is contained in:
parent
fc1eea59c0
commit
307acc745a
|
@ -57,6 +57,7 @@ Then, to leverage this in your migrations, do the following::
|
||||||
|
|
||||||
def forwards(apps, schema_editor):
|
def forwards(apps, schema_editor):
|
||||||
# Your migration code goes here
|
# Your migration code goes here
|
||||||
|
...
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
@ -83,14 +84,50 @@ 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
|
non-nullable :class:`~django.db.models.UUIDField` with a default value. Modify
|
||||||
the respective field according to your needs.
|
the respective field according to your needs.
|
||||||
|
|
||||||
* Add the field on your model with ``default=...`` and ``unique=True``
|
* Add the field on your model with ``default=uuid.uuid4`` and ``unique=True``
|
||||||
arguments. In the example, we use ``uuid.uuid4`` for the default.
|
arguments (choose an appropriate default for the type of the field you're
|
||||||
|
adding).
|
||||||
|
|
||||||
* Run the :djadmin:`makemigrations` command.
|
* Run the :djadmin:`makemigrations` command. This should generate a migration
|
||||||
|
with an ``AddField`` operation.
|
||||||
|
|
||||||
* Edit the created migration file.
|
* Generate two empty migration files for the same app by running
|
||||||
|
``makemigrations myapp --empty`` twice. We've renamed the migration files to
|
||||||
|
give them meaningful names in the examples below.
|
||||||
|
|
||||||
The generated migration class should look similar to this::
|
* Copy the ``AddField`` operation from the auto-generated migration (the first
|
||||||
|
of the three new files) to the last migration and change ``AddField`` to
|
||||||
|
``AlterField``. For example:
|
||||||
|
|
||||||
|
.. snippet::
|
||||||
|
:filename: 0006_remove_uuid_null.py
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('myapp', '0005_populate_uuid_values'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='mymodel',
|
||||||
|
name='uuid',
|
||||||
|
field=models.UUIDField(default=uuid.uuid4, unique=True),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
* Edit the first migration file. The generated migration class should look
|
||||||
|
similar to this:
|
||||||
|
|
||||||
|
.. snippet::
|
||||||
|
:filename: 0004_add_uuid_field.py
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
@ -102,25 +139,21 @@ the respective field according to your needs.
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='mymodel',
|
model_name='mymodel',
|
||||||
name='uuid',
|
name='uuid',
|
||||||
field=models.UUIDField(max_length=32, unique=True, default=uuid.uuid4),
|
field=models.UUIDField(default=uuid.uuid4, unique=True),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
You will need to make three changes:
|
Change ``unique=True`` to ``null=True`` -- this will create the intermediary
|
||||||
|
null field and defer creating the unique constraint until we've populated
|
||||||
|
unique values on all the rows.
|
||||||
|
|
||||||
* Add a second :class:`~django.db.migrations.operations.AddField` operation
|
* In the first empty migration file, add a
|
||||||
copied from the generated one and change it to
|
:class:`~django.db.migrations.operations.RunPython` or
|
||||||
:class:`~django.db.migrations.operations.AlterField`.
|
:class:`~django.db.migrations.operations.RunSQL` operation to generate a
|
||||||
|
unique value (UUID in the example) for each existing row. For example:
|
||||||
|
|
||||||
* On the first operation (``AddField``), change ``unique=True`` to
|
.. snippet::
|
||||||
``null=True`` -- this will create the intermediary null field.
|
:filename: 0005_populate_uuid_values.py
|
||||||
|
|
||||||
* 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 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
@ -137,25 +170,15 @@ the respective field according to your needs.
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
('myapp', '0003_auto_20150129_1705'),
|
('myapp', '0004_add_uuid_field'),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
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.
|
# omit reverse_code=... if you don't want the migration to be reversible.
|
||||||
migrations.RunPython(gen_uuid, reverse_code=migrations.RunPython.noop),
|
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.
|
* Now you can apply the migrations as usual with the :djadmin:`migrate` command.
|
||||||
|
|
||||||
Note there is a race condition if you allow objects to be created while this
|
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
|
migration is running. Objects created after the ``AddField`` and before
|
||||||
|
|
|
@ -322,11 +322,23 @@ or that you use :class:`SeparateDatabaseAndState` to add in operations that will
|
||||||
reflect your changes to the model state - otherwise, the versioned ORM and
|
reflect your changes to the model state - otherwise, the versioned ORM and
|
||||||
the autodetector will stop working correctly.
|
the autodetector will stop working correctly.
|
||||||
|
|
||||||
By default, ``RunPython`` will run its contents inside a transaction even
|
By default, ``RunPython`` will run its contents inside a transaction on
|
||||||
on databases that do not support DDL transactions (for example, MySQL and
|
databases that do not support DDL transactions (for example, MySQL and
|
||||||
Oracle). This should be safe, but may cause a crash if you attempt to use
|
Oracle). This should be safe, but may cause a crash if you attempt to use
|
||||||
the ``schema_editor`` provided on these backends; in this case, please
|
the ``schema_editor`` provided on these backends; in this case, pass
|
||||||
set ``atomic=False``.
|
``atomic=False`` to the ``RunPython`` operation.
|
||||||
|
|
||||||
|
On databases that do support DDL transactions (SQLite and PostgreSQL),
|
||||||
|
``RunPython`` operations do not have any transactions automatically added
|
||||||
|
besides the transactions created for each migration (the ``atomic`` parameter
|
||||||
|
has no effect on these databases). Thus, on PostgreSQL, for example, you should
|
||||||
|
avoid combining schema changes and ``RunPython`` operations in the same
|
||||||
|
migration or you may hit errors like ``OperationalError: cannot ALTER TABLE
|
||||||
|
"mytable" because it has pending trigger events``.
|
||||||
|
|
||||||
|
If you have a different database and aren't sure if it supports DDL
|
||||||
|
transactions, check the ``django.db.connection.features.can_rollback_ddl``
|
||||||
|
attribute.
|
||||||
|
|
||||||
.. warning::
|
.. warning::
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue