summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--django/db/migrations/operations/special.py5
-rw-r--r--tests/migrations/test_operations.py24
2 files changed, 24 insertions, 5 deletions
diff --git a/django/db/migrations/operations/special.py b/django/db/migrations/operations/special.py
index 714a6d59f9..40ae1f85b7 100644
--- a/django/db/migrations/operations/special.py
+++ b/django/db/migrations/operations/special.py
@@ -103,7 +103,6 @@ class RunPython(Operation):
"""
reduces_to_sql = False
- reversible = False
def __init__(self, code, reverse_code=None):
# Forwards code
@@ -118,6 +117,10 @@ class RunPython(Operation):
raise ValueError("RunPython must be supplied with callable arguments")
self.reverse_code = reverse_code
+ @property
+ def reversible(self):
+ return self.reverse_code is not None
+
def state_forwards(self, app_label, state):
# RunPython objects have no state effect. To add some, combine this
# with SeparateDatabaseAndState.
diff --git a/tests/migrations/test_operations.py b/tests/migrations/test_operations.py
index ef23dcf80f..c755ff0305 100644
--- a/tests/migrations/test_operations.py
+++ b/tests/migrations/test_operations.py
@@ -468,6 +468,7 @@ class OperationTests(MigrationTestBase):
operation.database_forwards("test_runsql", editor, project_state, new_state)
self.assertTableExists("i_love_ponies")
# And test reversal
+ self.assertTrue(operation.reversible)
with connection.schema_editor() as editor:
operation.database_backwards("test_runsql", editor, new_state, project_state)
self.assertTableNotExists("i_love_ponies")
@@ -484,7 +485,11 @@ class OperationTests(MigrationTestBase):
Pony = models.get_model("test_runpython", "Pony")
Pony.objects.create(pink=1, weight=3.55)
Pony.objects.create(weight=5)
- operation = migrations.RunPython(inner_method)
+ def inner_method_reverse(models, schema_editor):
+ Pony = models.get_model("test_runpython", "Pony")
+ Pony.objects.filter(pink=1, weight=3.55).delete()
+ Pony.objects.filter(weight=5).delete()
+ operation = migrations.RunPython(inner_method, reverse_code=inner_method_reverse)
# Test the state alteration does nothing
new_state = project_state.clone()
operation.state_forwards("test_runpython", new_state)
@@ -494,13 +499,24 @@ class OperationTests(MigrationTestBase):
with connection.schema_editor() as editor:
operation.database_forwards("test_runpython", editor, project_state, new_state)
self.assertEqual(project_state.render().get_model("test_runpython", "Pony").objects.count(), 2)
- # And test reversal fails
- with self.assertRaises(NotImplementedError):
- operation.database_backwards("test_runpython", None, new_state, project_state)
+ # Now test reversal
+ self.assertTrue(operation.reversible)
+ with connection.schema_editor() as editor:
+ operation.database_backwards("test_runpython", editor, project_state, new_state)
+ self.assertEqual(project_state.render().get_model("test_runpython", "Pony").objects.count(), 0)
# Now test we can't use a string
with self.assertRaises(ValueError):
operation = migrations.RunPython("print 'ahahaha'")
+ # Also test reversal fails, with an operation identical to above but without reverse_code set
+ no_reverse_operation = migrations.RunPython(inner_method)
+ self.assertFalse(no_reverse_operation.reversible)
+ with connection.schema_editor() as editor:
+ no_reverse_operation.database_forwards("test_runpython", editor, project_state, new_state)
+ with self.assertRaises(NotImplementedError):
+ no_reverse_operation.database_backwards("test_runpython", editor, new_state, project_state)
+
+
class MigrateNothingRouter(object):
"""