Fixed #22095 -- Enabled backward migrations for RunPython operations

Added reversible property to RunPython so that migrations will not
refuse to reverse migrations including RunPython operations, so long as
reverse_code is set in the RunPython constructor. Included tests to
check the reversible property on RunPython and the similar RunSQL.
This commit is contained in:
Andrew Gorcester 2014-02-19 15:44:57 -08:00 committed by Baptiste Mispelon
parent b887408486
commit 202bf69c2f
2 changed files with 24 additions and 5 deletions

View File

@ -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.

View File

@ -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):
"""