diff --git a/django/db/migrations/operations/special.py b/django/db/migrations/operations/special.py index 2c995c54ac..e1ce9a07b5 100644 --- a/django/db/migrations/operations/special.py +++ b/django/db/migrations/operations/special.py @@ -13,6 +13,8 @@ class SeparateDatabaseAndState(Operation): that affect the state or not the database, or so on. """ + serialization_expand_args = ['database_operations', 'state_operations'] + def __init__(self, database_operations=None, state_operations=None): self.database_operations = database_operations or [] self.state_operations = state_operations or [] diff --git a/django/db/migrations/writer.py b/django/db/migrations/writer.py index c91ee6870f..24b59bc7f3 100644 --- a/django/db/migrations/writer.py +++ b/django/db/migrations/writer.py @@ -39,11 +39,10 @@ class SettingsReference(str): class OperationWriter(object): - indentation = 2 - - def __init__(self, operation): + def __init__(self, operation, indentation=2): self.operation = operation self.buff = [] + self.indentation = indentation def serialize(self): @@ -56,7 +55,14 @@ class OperationWriter(object): for key, value in _arg_value.items(): key_string, key_imports = MigrationWriter.serialize(key) arg_string, arg_imports = MigrationWriter.serialize(value) - self.feed('%s: %s,' % (key_string, arg_string)) + args = arg_string.splitlines() + if len(args) > 1: + self.feed('%s: %s' % (key_string, args[0])) + for arg in args[1:-1]: + self.feed(arg) + self.feed('%s,' % args[-1]) + else: + self.feed('%s: %s,' % (key_string, arg_string)) imports.update(key_imports) imports.update(arg_imports) self.unindent() @@ -66,13 +72,26 @@ class OperationWriter(object): self.indent() for item in _arg_value: arg_string, arg_imports = MigrationWriter.serialize(item) - self.feed('%s,' % arg_string) + args = arg_string.splitlines() + if len(args) > 1: + for arg in args[:-1]: + self.feed(arg) + self.feed('%s,' % args[-1]) + else: + self.feed('%s,' % arg_string) imports.update(arg_imports) self.unindent() self.feed('],') else: arg_string, arg_imports = MigrationWriter.serialize(_arg_value) - self.feed('%s=%s,' % (_arg_name, arg_string)) + args = arg_string.splitlines() + if len(args) > 1: + self.feed('%s=%s' % (_arg_name, args[0])) + for arg in args[1:-1]: + self.feed(arg) + self.feed('%s,' % args[-1]) + else: + self.feed('%s=%s,' % (_arg_name, arg_string)) imports.update(arg_imports) imports = set() diff --git a/tests/migrations/test_writer.py b/tests/migrations/test_writer.py index 712fb39fea..b60d6e79da 100644 --- a/tests/migrations/test_writer.py +++ b/tests/migrations/test_writer.py @@ -37,9 +37,7 @@ class OperationWriterTests(SimpleTestCase): def test_empty_signature(self): operation = custom_migration_operations.operations.TestOperation() - writer = OperationWriter(operation) - writer.indentation = 0 - buff, imports = writer.serialize() + buff, imports = OperationWriter(operation, indentation=0).serialize() self.assertEqual(imports, {'import custom_migration_operations.operations'}) self.assertEqual( buff, @@ -49,9 +47,7 @@ class OperationWriterTests(SimpleTestCase): def test_args_signature(self): operation = custom_migration_operations.operations.ArgsOperation(1, 2) - writer = OperationWriter(operation) - writer.indentation = 0 - buff, imports = writer.serialize() + buff, imports = OperationWriter(operation, indentation=0).serialize() self.assertEqual(imports, {'import custom_migration_operations.operations'}) self.assertEqual( buff, @@ -63,9 +59,7 @@ class OperationWriterTests(SimpleTestCase): def test_kwargs_signature(self): operation = custom_migration_operations.operations.KwargsOperation(kwarg1=1) - writer = OperationWriter(operation) - writer.indentation = 0 - buff, imports = writer.serialize() + buff, imports = OperationWriter(operation, indentation=0).serialize() self.assertEqual(imports, {'import custom_migration_operations.operations'}) self.assertEqual( buff, @@ -76,9 +70,7 @@ class OperationWriterTests(SimpleTestCase): def test_args_kwargs_signature(self): operation = custom_migration_operations.operations.ArgsKwargsOperation(1, 2, kwarg2=4) - writer = OperationWriter(operation) - writer.indentation = 0 - buff, imports = writer.serialize() + buff, imports = OperationWriter(operation, indentation=0).serialize() self.assertEqual(imports, {'import custom_migration_operations.operations'}) self.assertEqual( buff, @@ -89,11 +81,21 @@ class OperationWriterTests(SimpleTestCase): '),' ) + def test_multiline_args_signature(self): + operation = custom_migration_operations.operations.ArgsOperation("test\n arg1", "test\narg2") + buff, imports = OperationWriter(operation, indentation=0).serialize() + self.assertEqual(imports, {'import custom_migration_operations.operations'}) + self.assertEqual( + buff, + "custom_migration_operations.operations.ArgsOperation(\n" + " arg1='test\\n arg1',\n" + " arg2='test\\narg2',\n" + ")," + ) + def test_expand_args_signature(self): operation = custom_migration_operations.operations.ExpandArgsOperation([1, 2]) - writer = OperationWriter(operation) - writer.indentation = 0 - buff, imports = writer.serialize() + buff, imports = OperationWriter(operation, indentation=0).serialize() self.assertEqual(imports, {'import custom_migration_operations.operations'}) self.assertEqual( buff, @@ -159,6 +161,14 @@ class WriterTests(TestCase): string, imports = MigrationWriter.serialize("foobar") self.assertEqual(string, "'foobar'") + def test_serialize_multiline_strings(self): + self.assertSerializedEqual(b"foo\nbar") + string, imports = MigrationWriter.serialize(b"foo\nbar") + self.assertEqual(string, "b'foo\\nbar'") + self.assertSerializedEqual("föo\nbár") + string, imports = MigrationWriter.serialize("foo\nbar") + self.assertEqual(string, "'foo\\nbar'") + def test_serialize_collections(self): self.assertSerializedEqual({1: 2}) self.assertSerializedEqual(["a", 2, True, None])