瀏覽代碼

Fixed #24630 -- Clarified docs about RunPython transactions.

Thanks Markus Holtermann for review.
Tim Graham 10 年之前
父節點
當前提交
307acc745a
共有 2 個文件被更改,包括 70 次插入35 次删除
  1. 54 31
      docs/howto/writing-migrations.txt
  2. 16 4
      docs/ref/migration-operations.txt

+ 54 - 31
docs/howto/writing-migrations.txt

@@ -57,6 +57,7 @@ Then, to leverage this in your migrations, do the following::
 
     def forwards(apps, schema_editor):
         # Your migration code goes here
+        ...
 
     class Migration(migrations.Migration):
 
@@ -83,44 +84,76 @@ 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.
+* Add the field on your model with ``default=uuid.uuid4`` and ``unique=True``
+  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.
+
+* 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
 
-  The generated migration class should look similar to this::
 
     class Migration(migrations.Migration):
 
         dependencies = [
-            ('myapp', '0003_auto_20150129_1705'),
+            ('myapp', '0005_populate_uuid_values'),
         ]
 
         operations = [
-            migrations.AddField(
+            migrations.AlterField(
                 model_name='mymodel',
                 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:
+* 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):
+
+        dependencies = [
+            ('myapp', '0003_auto_20150129_1705'),
+        ]
 
-  * 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`.
+        operations = [
+            migrations.AddField(
+                model_name='mymodel',
+                name='uuid',
+                field=models.UUIDField(default=uuid.uuid4, unique=True),
+            ),
+        ]
 
-  * On the first operation (``AddField``), change ``unique=True`` to
-    ``null=True`` -- this will create the intermediary null field.
+  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.
 
-  * 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.
+* In the first empty migration file, 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. For example:
 
-  The resulting migration should look similar to this::
+  .. snippet::
+    :filename: 0005_populate_uuid_values.py
 
     # -*- coding: utf-8 -*-
     from __future__ import unicode_literals
@@ -137,25 +170,15 @@ the respective field according to your needs.
     class Migration(migrations.Migration):
 
         dependencies = [
-            ('myapp', '0003_auto_20150129_1705'),
+            ('myapp', '0004_add_uuid_field'),
         ]
 
         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.
+* 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
   migration is running. Objects created after the ``AddField`` and before

+ 16 - 4
docs/ref/migration-operations.txt

@@ -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
 the autodetector will stop working correctly.
 
-By default, ``RunPython`` will run its contents inside a transaction even
-on databases that do not support DDL transactions (for example, MySQL and
+By default, ``RunPython`` will run its contents inside a transaction on
+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
-the ``schema_editor`` provided on these backends; in this case, please
-set ``atomic=False``.
+the ``schema_editor`` provided on these backends; in this case, pass
+``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::