Fixed #24098 -- Added no-op attributes to RunPython and RunSQL
Thanks Loïc Bistuer and Tim Graham for the discussion and review.
This commit is contained in:
parent
67d6a8c4e6
commit
c8bac4b556
|
@ -61,6 +61,7 @@ class RunSQL(Operation):
|
||||||
Also accepts a list of operations that represent the state change effected
|
Also accepts a list of operations that represent the state change effected
|
||||||
by this SQL change, in case it's custom column/table creation/deletion.
|
by this SQL change, in case it's custom column/table creation/deletion.
|
||||||
"""
|
"""
|
||||||
|
noop = ''
|
||||||
|
|
||||||
def __init__(self, sql, reverse_sql=None, state_operations=None):
|
def __init__(self, sql, reverse_sql=None, state_operations=None):
|
||||||
self.sql = sql
|
self.sql = sql
|
||||||
|
@ -100,9 +101,9 @@ class RunSQL(Operation):
|
||||||
def describe(self):
|
def describe(self):
|
||||||
return "Raw SQL operation"
|
return "Raw SQL operation"
|
||||||
|
|
||||||
def _run_sql(self, schema_editor, sql):
|
def _run_sql(self, schema_editor, sqls):
|
||||||
if isinstance(sql, (list, tuple)):
|
if isinstance(sqls, (list, tuple)):
|
||||||
for sql in sql:
|
for sql in sqls:
|
||||||
params = None
|
params = None
|
||||||
if isinstance(sql, (list, tuple)):
|
if isinstance(sql, (list, tuple)):
|
||||||
elements = len(sql)
|
elements = len(sql)
|
||||||
|
@ -111,8 +112,8 @@ class RunSQL(Operation):
|
||||||
else:
|
else:
|
||||||
raise ValueError("Expected a 2-tuple but got %d" % elements)
|
raise ValueError("Expected a 2-tuple but got %d" % elements)
|
||||||
schema_editor.execute(sql, params=params)
|
schema_editor.execute(sql, params=params)
|
||||||
else:
|
elif sqls != RunSQL.noop:
|
||||||
statements = schema_editor.connection.ops.prepare_sql_script(sql)
|
statements = schema_editor.connection.ops.prepare_sql_script(sqls)
|
||||||
for statement in statements:
|
for statement in statements:
|
||||||
schema_editor.execute(statement, params=None)
|
schema_editor.execute(statement, params=None)
|
||||||
|
|
||||||
|
@ -175,3 +176,7 @@ class RunPython(Operation):
|
||||||
|
|
||||||
def describe(self):
|
def describe(self):
|
||||||
return "Raw Python operation"
|
return "Raw Python operation"
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def noop(apps, schema_editor):
|
||||||
|
return None
|
||||||
|
|
|
@ -245,6 +245,14 @@ operation that adds that field and so will try to run it again).
|
||||||
The ability to pass parameters to the ``sql`` and ``reverse_sql`` queries
|
The ability to pass parameters to the ``sql`` and ``reverse_sql`` queries
|
||||||
was added.
|
was added.
|
||||||
|
|
||||||
|
.. attribute:: RunSQL.noop
|
||||||
|
|
||||||
|
.. versionadded:: 1.8
|
||||||
|
|
||||||
|
Pass the ``RunSQL.noop`` attribute to ``sql`` or ``reverse_sql`` when you
|
||||||
|
want the operation not to do anything in the given direction. This is
|
||||||
|
especially useful in making the operation reversible.
|
||||||
|
|
||||||
.. _sqlparse: https://pypi.python.org/pypi/sqlparse
|
.. _sqlparse: https://pypi.python.org/pypi/sqlparse
|
||||||
|
|
||||||
RunPython
|
RunPython
|
||||||
|
@ -321,6 +329,14 @@ set ``atomic=False``.
|
||||||
``schema_editor.connection.alias``, where ``schema_editor`` is the second
|
``schema_editor.connection.alias``, where ``schema_editor`` is the second
|
||||||
argument to your function).
|
argument to your function).
|
||||||
|
|
||||||
|
.. staticmethod:: RunPython.noop
|
||||||
|
|
||||||
|
.. versionadded:: 1.8
|
||||||
|
|
||||||
|
Pass the ``RunPython.noop`` method to ``code`` or ``reverse_code`` when
|
||||||
|
you want the operation not to do anything in the given direction. This is
|
||||||
|
especially useful in making the operation reversible.
|
||||||
|
|
||||||
SeparateDatabaseAndState
|
SeparateDatabaseAndState
|
||||||
------------------------
|
------------------------
|
||||||
|
|
||||||
|
|
|
@ -457,6 +457,11 @@ Migrations
|
||||||
* A :ref:`generic mechanism to handle the deprecation of model fields
|
* A :ref:`generic mechanism to handle the deprecation of model fields
|
||||||
<migrations-removing-model-fields>` was added.
|
<migrations-removing-model-fields>` was added.
|
||||||
|
|
||||||
|
* The :attr:`RunPython.noop <django.db.migrations.operations.RunPython.noop>`
|
||||||
|
and :meth:`RunSQL.noop() <django.db.migrations.operations.RunSQL.noop>` class
|
||||||
|
attribute/method were added to ease in making ``RunPython`` and ``RunSQL``
|
||||||
|
operations reversible.
|
||||||
|
|
||||||
Models
|
Models
|
||||||
^^^^^^
|
^^^^^^
|
||||||
|
|
||||||
|
|
|
@ -7,5 +7,5 @@ from django.db import migrations
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.RunPython(lambda apps, schema_editor: None)
|
migrations.RunPython(migrations.RunPython.noop)
|
||||||
]
|
]
|
||||||
|
|
|
@ -9,5 +9,5 @@ class Migration(migrations.Migration):
|
||||||
dependencies = [("migrations", "1_auto")]
|
dependencies = [("migrations", "1_auto")]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.RunPython(lambda apps, schema_editor: None)
|
migrations.RunPython(migrations.RunPython.noop)
|
||||||
]
|
]
|
||||||
|
|
|
@ -9,5 +9,5 @@ class Migration(migrations.Migration):
|
||||||
dependencies = [("migrations", "2_auto")]
|
dependencies = [("migrations", "2_auto")]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.RunPython(lambda apps, schema_editor: None)
|
migrations.RunPython(migrations.RunPython.noop)
|
||||||
]
|
]
|
||||||
|
|
|
@ -15,5 +15,5 @@ class Migration(migrations.Migration):
|
||||||
dependencies = [("migrations", "2_auto")]
|
dependencies = [("migrations", "2_auto")]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.RunPython(lambda apps, schema_editor: None)
|
migrations.RunPython(migrations.RunPython.noop)
|
||||||
]
|
]
|
||||||
|
|
|
@ -9,5 +9,5 @@ class Migration(migrations.Migration):
|
||||||
dependencies = [("migrations", "3_auto")]
|
dependencies = [("migrations", "3_auto")]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.RunPython(lambda apps, schema_editor: None)
|
migrations.RunPython(migrations.RunPython.noop)
|
||||||
]
|
]
|
||||||
|
|
|
@ -9,5 +9,5 @@ class Migration(migrations.Migration):
|
||||||
dependencies = [("migrations", "4_auto")]
|
dependencies = [("migrations", "4_auto")]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.RunPython(lambda apps, schema_editor: None)
|
migrations.RunPython(migrations.RunPython.noop)
|
||||||
]
|
]
|
||||||
|
|
|
@ -9,5 +9,5 @@ class Migration(migrations.Migration):
|
||||||
dependencies = [("migrations", "5_auto")]
|
dependencies = [("migrations", "5_auto")]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.RunPython(lambda apps, schema_editor: None)
|
migrations.RunPython(migrations.RunPython.noop)
|
||||||
]
|
]
|
||||||
|
|
|
@ -9,5 +9,5 @@ class Migration(migrations.Migration):
|
||||||
dependencies = [("migrations", "6_auto")]
|
dependencies = [("migrations", "6_auto")]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.RunPython(lambda apps, schema_editor: None)
|
migrations.RunPython(migrations.RunPython.noop)
|
||||||
]
|
]
|
||||||
|
|
|
@ -7,5 +7,5 @@ from django.db import migrations
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.RunPython(lambda apps, schema_editor: None)
|
migrations.RunPython(migrations.RunPython.noop)
|
||||||
]
|
]
|
||||||
|
|
|
@ -9,5 +9,5 @@ class Migration(migrations.Migration):
|
||||||
dependencies = [("app1", "1_auto")]
|
dependencies = [("app1", "1_auto")]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.RunPython(lambda apps, schema_editor: None)
|
migrations.RunPython(migrations.RunPython.noop)
|
||||||
]
|
]
|
||||||
|
|
|
@ -14,5 +14,5 @@ class Migration(migrations.Migration):
|
||||||
dependencies = [("app1", "1_auto"), ("app2", "2_auto")]
|
dependencies = [("app1", "1_auto"), ("app2", "2_auto")]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.RunPython(lambda apps, schema_editor: None)
|
migrations.RunPython(migrations.RunPython.noop)
|
||||||
]
|
]
|
||||||
|
|
|
@ -9,5 +9,5 @@ class Migration(migrations.Migration):
|
||||||
dependencies = [("app1", "2_auto"), ("app2", "2_auto")]
|
dependencies = [("app1", "2_auto"), ("app2", "2_auto")]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.RunPython(lambda apps, schema_editor: None)
|
migrations.RunPython(migrations.RunPython.noop)
|
||||||
]
|
]
|
||||||
|
|
|
@ -9,5 +9,5 @@ class Migration(migrations.Migration):
|
||||||
dependencies = [("app1", "3_auto")]
|
dependencies = [("app1", "3_auto")]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.RunPython(lambda apps, schema_editor: None)
|
migrations.RunPython(migrations.RunPython.noop)
|
||||||
]
|
]
|
||||||
|
|
|
@ -9,5 +9,5 @@ class Migration(migrations.Migration):
|
||||||
dependencies = [("app1", "1_auto")]
|
dependencies = [("app1", "1_auto")]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.RunPython(lambda apps, schema_editor: None)
|
migrations.RunPython(migrations.RunPython.noop)
|
||||||
]
|
]
|
||||||
|
|
|
@ -14,5 +14,5 @@ class Migration(migrations.Migration):
|
||||||
dependencies = [("app1", "1_auto")]
|
dependencies = [("app1", "1_auto")]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.RunPython(lambda apps, schema_editor: None)
|
migrations.RunPython(migrations.RunPython.noop)
|
||||||
]
|
]
|
||||||
|
|
|
@ -9,5 +9,5 @@ class Migration(migrations.Migration):
|
||||||
dependencies = [("app2", "1_auto")]
|
dependencies = [("app2", "1_auto")]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.RunPython(lambda apps, schema_editor: None)
|
migrations.RunPython(migrations.RunPython.noop)
|
||||||
]
|
]
|
||||||
|
|
|
@ -7,5 +7,5 @@ from django.db import migrations
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.RunPython(lambda apps, schema_editor: None)
|
migrations.RunPython(migrations.RunPython.noop)
|
||||||
]
|
]
|
||||||
|
|
|
@ -9,5 +9,5 @@ class Migration(migrations.Migration):
|
||||||
dependencies = [("migrations", "1_auto")]
|
dependencies = [("migrations", "1_auto")]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.RunPython(lambda apps, schema_editor: None)
|
migrations.RunPython(migrations.RunPython.noop)
|
||||||
]
|
]
|
||||||
|
|
|
@ -15,5 +15,5 @@ class Migration(migrations.Migration):
|
||||||
dependencies = [("migrations", "2_auto")]
|
dependencies = [("migrations", "2_auto")]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.RunPython(lambda apps, schema_editor: None)
|
migrations.RunPython(migrations.RunPython.noop)
|
||||||
]
|
]
|
||||||
|
|
|
@ -9,5 +9,5 @@ class Migration(migrations.Migration):
|
||||||
dependencies = [("migrations", "5_auto")]
|
dependencies = [("migrations", "5_auto")]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.RunPython(lambda apps, schema_editor: None)
|
migrations.RunPython(migrations.RunPython.noop)
|
||||||
]
|
]
|
||||||
|
|
|
@ -9,5 +9,5 @@ class Migration(migrations.Migration):
|
||||||
dependencies = [("migrations", "6_auto")]
|
dependencies = [("migrations", "6_auto")]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.RunPython(lambda apps, schema_editor: None)
|
migrations.RunPython(migrations.RunPython.noop)
|
||||||
]
|
]
|
||||||
|
|
|
@ -1494,6 +1494,15 @@ class OperationTests(OperationTestBase):
|
||||||
operation.database_backwards,
|
operation.database_backwards,
|
||||||
"test_runsql", editor, new_state, project_state)
|
"test_runsql", editor, new_state, project_state)
|
||||||
|
|
||||||
|
def test_run_sql_noop(self):
|
||||||
|
"""
|
||||||
|
#24098 - Tests no-op RunSQL operations.
|
||||||
|
"""
|
||||||
|
operation = migrations.RunSQL(migrations.RunSQL.noop, migrations.RunSQL.noop)
|
||||||
|
with connection.schema_editor() as editor:
|
||||||
|
operation.database_forwards("test_runsql", editor, None, None)
|
||||||
|
operation.database_backwards("test_runsql", editor, None, None)
|
||||||
|
|
||||||
def test_run_python(self):
|
def test_run_python(self):
|
||||||
"""
|
"""
|
||||||
Tests the RunPython operation
|
Tests the RunPython operation
|
||||||
|
@ -1620,6 +1629,17 @@ class OperationTests(OperationTestBase):
|
||||||
self.assertEqual(definition[1], [])
|
self.assertEqual(definition[1], [])
|
||||||
self.assertEqual(sorted(definition[2]), ["atomic", "code"])
|
self.assertEqual(sorted(definition[2]), ["atomic", "code"])
|
||||||
|
|
||||||
|
def test_run_python_noop(self):
|
||||||
|
"""
|
||||||
|
#24098 - Tests no-op RunPython operations.
|
||||||
|
"""
|
||||||
|
project_state = ProjectState()
|
||||||
|
new_state = project_state.clone()
|
||||||
|
operation = migrations.RunPython(migrations.RunPython.noop, migrations.RunPython.noop)
|
||||||
|
with connection.schema_editor() as editor:
|
||||||
|
operation.database_forwards("test_runpython", editor, project_state, new_state)
|
||||||
|
operation.database_backwards("test_runpython", editor, new_state, project_state)
|
||||||
|
|
||||||
@unittest.skipIf(sqlparse is None and connection.features.requires_sqlparse_for_splitting, "Missing sqlparse")
|
@unittest.skipIf(sqlparse is None and connection.features.requires_sqlparse_for_splitting, "Missing sqlparse")
|
||||||
def test_separate_database_and_state(self):
|
def test_separate_database_and_state(self):
|
||||||
"""
|
"""
|
||||||
|
|
Loading…
Reference in New Issue