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:
parent
b887408486
commit
202bf69c2f
|
@ -103,7 +103,6 @@ class RunPython(Operation):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
reduces_to_sql = False
|
reduces_to_sql = False
|
||||||
reversible = False
|
|
||||||
|
|
||||||
def __init__(self, code, reverse_code=None):
|
def __init__(self, code, reverse_code=None):
|
||||||
# Forwards code
|
# Forwards code
|
||||||
|
@ -118,6 +117,10 @@ class RunPython(Operation):
|
||||||
raise ValueError("RunPython must be supplied with callable arguments")
|
raise ValueError("RunPython must be supplied with callable arguments")
|
||||||
self.reverse_code = reverse_code
|
self.reverse_code = reverse_code
|
||||||
|
|
||||||
|
@property
|
||||||
|
def reversible(self):
|
||||||
|
return self.reverse_code is not None
|
||||||
|
|
||||||
def state_forwards(self, app_label, state):
|
def state_forwards(self, app_label, state):
|
||||||
# RunPython objects have no state effect. To add some, combine this
|
# RunPython objects have no state effect. To add some, combine this
|
||||||
# with SeparateDatabaseAndState.
|
# with SeparateDatabaseAndState.
|
||||||
|
|
|
@ -468,6 +468,7 @@ class OperationTests(MigrationTestBase):
|
||||||
operation.database_forwards("test_runsql", editor, project_state, new_state)
|
operation.database_forwards("test_runsql", editor, project_state, new_state)
|
||||||
self.assertTableExists("i_love_ponies")
|
self.assertTableExists("i_love_ponies")
|
||||||
# And test reversal
|
# And test reversal
|
||||||
|
self.assertTrue(operation.reversible)
|
||||||
with connection.schema_editor() as editor:
|
with connection.schema_editor() as editor:
|
||||||
operation.database_backwards("test_runsql", editor, new_state, project_state)
|
operation.database_backwards("test_runsql", editor, new_state, project_state)
|
||||||
self.assertTableNotExists("i_love_ponies")
|
self.assertTableNotExists("i_love_ponies")
|
||||||
|
@ -484,7 +485,11 @@ class OperationTests(MigrationTestBase):
|
||||||
Pony = models.get_model("test_runpython", "Pony")
|
Pony = models.get_model("test_runpython", "Pony")
|
||||||
Pony.objects.create(pink=1, weight=3.55)
|
Pony.objects.create(pink=1, weight=3.55)
|
||||||
Pony.objects.create(weight=5)
|
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
|
# Test the state alteration does nothing
|
||||||
new_state = project_state.clone()
|
new_state = project_state.clone()
|
||||||
operation.state_forwards("test_runpython", new_state)
|
operation.state_forwards("test_runpython", new_state)
|
||||||
|
@ -494,13 +499,24 @@ class OperationTests(MigrationTestBase):
|
||||||
with connection.schema_editor() as editor:
|
with connection.schema_editor() as editor:
|
||||||
operation.database_forwards("test_runpython", editor, project_state, new_state)
|
operation.database_forwards("test_runpython", editor, project_state, new_state)
|
||||||
self.assertEqual(project_state.render().get_model("test_runpython", "Pony").objects.count(), 2)
|
self.assertEqual(project_state.render().get_model("test_runpython", "Pony").objects.count(), 2)
|
||||||
# And test reversal fails
|
# Now test reversal
|
||||||
with self.assertRaises(NotImplementedError):
|
self.assertTrue(operation.reversible)
|
||||||
operation.database_backwards("test_runpython", None, new_state, project_state)
|
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
|
# Now test we can't use a string
|
||||||
with self.assertRaises(ValueError):
|
with self.assertRaises(ValueError):
|
||||||
operation = migrations.RunPython("print 'ahahaha'")
|
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):
|
class MigrateNothingRouter(object):
|
||||||
"""
|
"""
|
||||||
|
|
Loading…
Reference in New Issue