mirror of
				https://github.com/django/django.git
				synced 2025-10-31 01:25:32 +00:00 
			
		
		
		
	Fixed #21323 -- Improved readability of serialized Operation.
This commit is contained in:
		| @@ -21,6 +21,8 @@ class Operation(object): | |||||||
|     # Can this migration be represented as SQL? (things like RunPython cannot) |     # Can this migration be represented as SQL? (things like RunPython cannot) | ||||||
|     reduces_to_sql = True |     reduces_to_sql = True | ||||||
|  |  | ||||||
|  |     serialization_expand_args = [] | ||||||
|  |  | ||||||
|     def __new__(cls, *args, **kwargs): |     def __new__(cls, *args, **kwargs): | ||||||
|         # We capture the arguments to make returning them trivial |         # We capture the arguments to make returning them trivial | ||||||
|         self = object.__new__(cls) |         self = object.__new__(cls) | ||||||
|   | |||||||
| @@ -1,8 +1,8 @@ | |||||||
| from .base import Operation |  | ||||||
| from django.utils import six |  | ||||||
| from django.db import models, router | from django.db import models, router | ||||||
| from django.db.models.options import normalize_unique_together | from django.db.models.options import normalize_unique_together | ||||||
| from django.db.migrations.state import ModelState | from django.db.migrations.state import ModelState | ||||||
|  | from django.db.migrations.operations.base import Operation | ||||||
|  | from django.utils import six | ||||||
|  |  | ||||||
|  |  | ||||||
| class CreateModel(Operation): | class CreateModel(Operation): | ||||||
| @@ -10,6 +10,8 @@ class CreateModel(Operation): | |||||||
|     Create a model's table. |     Create a model's table. | ||||||
|     """ |     """ | ||||||
|  |  | ||||||
|  |     serialization_expand_args = ['fields', 'options'] | ||||||
|  |  | ||||||
|     def __init__(self, name, fields, options=None, bases=None): |     def __init__(self, name, fields, options=None, bases=None): | ||||||
|         self.name = name |         self.name = name | ||||||
|         self.fields = fields |         self.fields = fields | ||||||
|   | |||||||
| @@ -1,6 +1,7 @@ | |||||||
| from __future__ import unicode_literals | from __future__ import unicode_literals | ||||||
|  |  | ||||||
| import datetime | import datetime | ||||||
|  | import inspect | ||||||
| from importlib import import_module | from importlib import import_module | ||||||
| import os | import os | ||||||
| import types | import types | ||||||
| @@ -27,6 +28,64 @@ class SettingsReference(str): | |||||||
|         self.setting_name = setting_name |         self.setting_name = setting_name | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class OperationWriter(object): | ||||||
|  |     indentation = 2 | ||||||
|  |  | ||||||
|  |     def __init__(self, operation): | ||||||
|  |         self.operation = operation | ||||||
|  |         self.buff = [] | ||||||
|  |  | ||||||
|  |     def serialize(self): | ||||||
|  |         imports = set() | ||||||
|  |         name, args, kwargs = self.operation.deconstruct() | ||||||
|  |         argspec = inspect.getargspec(self.operation.__init__) | ||||||
|  |         normalized_kwargs = inspect.getcallargs(self.operation.__init__, *args, **kwargs) | ||||||
|  |  | ||||||
|  |         self.feed('migrations.%s(' % name) | ||||||
|  |         self.indent() | ||||||
|  |         for arg_name in argspec.args[1:]: | ||||||
|  |             arg_value = normalized_kwargs[arg_name] | ||||||
|  |             if (arg_name in self.operation.serialization_expand_args and | ||||||
|  |                     isinstance(arg_value, (list, tuple, dict))): | ||||||
|  |                 if isinstance(arg_value, dict): | ||||||
|  |                     self.feed('%s={' % arg_name) | ||||||
|  |                     self.indent() | ||||||
|  |                     for key, value in arg_value.items(): | ||||||
|  |                         arg_string, arg_imports = MigrationWriter.serialize(value) | ||||||
|  |                         self.feed('%s: %s,' % (repr(key), arg_string)) | ||||||
|  |                         imports.update(arg_imports) | ||||||
|  |                     self.unindent() | ||||||
|  |                     self.feed('},') | ||||||
|  |                 else: | ||||||
|  |                     self.feed('%s=[' % arg_name) | ||||||
|  |                     self.indent() | ||||||
|  |                     for item in arg_value: | ||||||
|  |                         arg_string, arg_imports = MigrationWriter.serialize(item) | ||||||
|  |                         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)) | ||||||
|  |                 imports.update(arg_imports) | ||||||
|  |         self.unindent() | ||||||
|  |         self.feed('),') | ||||||
|  |         return self.render(), imports | ||||||
|  |  | ||||||
|  |     def indent(self): | ||||||
|  |         self.indentation += 1 | ||||||
|  |  | ||||||
|  |     def unindent(self): | ||||||
|  |         self.indentation -= 1 | ||||||
|  |  | ||||||
|  |     def feed(self, line): | ||||||
|  |         self.buff.append(' ' * (self.indentation * 4) + line) | ||||||
|  |  | ||||||
|  |     def render(self): | ||||||
|  |         return '\n'.join(self.buff) | ||||||
|  |  | ||||||
|  |  | ||||||
| class MigrationWriter(object): | class MigrationWriter(object): | ||||||
|     """ |     """ | ||||||
|     Takes a Migration instance and is able to produce the contents |     Takes a Migration instance and is able to produce the contents | ||||||
| @@ -43,40 +102,35 @@ class MigrationWriter(object): | |||||||
|         items = { |         items = { | ||||||
|             "replaces_str": "", |             "replaces_str": "", | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         imports = set() |         imports = set() | ||||||
|  |  | ||||||
|         # Deconstruct operations |         # Deconstruct operations | ||||||
|         operation_strings = [] |         operations = [] | ||||||
|         for operation in self.migration.operations: |         for operation in self.migration.operations: | ||||||
|             name, args, kwargs = operation.deconstruct() |             operation_string, operation_imports = OperationWriter(operation).serialize() | ||||||
|             arg_strings = [] |             imports.update(operation_imports) | ||||||
|             for arg in args: |             operations.append(operation_string) | ||||||
|                 arg_string, arg_imports = self.serialize(arg) |         items["operations"] = "\n".join(operations) + "\n" if operations else "" | ||||||
|                 arg_strings.append(arg_string) |  | ||||||
|                 imports.update(arg_imports) |  | ||||||
|             for kw, arg in kwargs.items(): |  | ||||||
|                 arg_string, arg_imports = self.serialize(arg) |  | ||||||
|                 imports.update(arg_imports) |  | ||||||
|                 arg_strings.append("%s = %s" % (kw, arg_string)) |  | ||||||
|             operation_strings.append("migrations.%s(%s\n        )" % (name, "".join("\n            %s," % arg for arg in arg_strings))) |  | ||||||
|         items["operations"] = "[%s\n    ]" % "".join("\n        %s," % s for s in operation_strings) |  | ||||||
|         # Format dependencies and write out swappable dependencies right |         # Format dependencies and write out swappable dependencies right | ||||||
|         items["dependencies"] = "[" |         dependencies = [] | ||||||
|         for dependency in self.migration.dependencies: |         for dependency in self.migration.dependencies: | ||||||
|             if dependency[0] == "__setting__": |             if dependency[0] == "__setting__": | ||||||
|                 items["dependencies"] += "\n        migrations.swappable_dependency(settings.%s)," % dependency[1] |                 dependencies.append("        migrations.swappable_dependency(settings.%s)," % dependency[1]) | ||||||
|                 imports.add("from django.conf import settings") |                 imports.add("from django.conf import settings") | ||||||
|             else: |             else: | ||||||
|                 items["dependencies"] += "\n        %s," % repr(dependency) |                 dependencies.append("        %s," % repr(dependency)) | ||||||
|         items["dependencies"] += "\n    ]" |         items["dependencies"] = "\n".join(dependencies) + "\n" if dependencies else "" | ||||||
|  |  | ||||||
|         # Format imports nicely |         # Format imports nicely | ||||||
|         imports.discard("from django.db import models") |         imports.discard("from django.db import models") | ||||||
|         if not imports: |         items["imports"] = "\n".join(imports) + "\n" if imports else "" | ||||||
|             items["imports"] = "" |  | ||||||
|         else: |  | ||||||
|             items["imports"] = "\n".join(imports) + "\n" |  | ||||||
|         # If there's a replaces, make a string for it |         # If there's a replaces, make a string for it | ||||||
|         if self.migration.replaces: |         if self.migration.replaces: | ||||||
|             items['replaces_str'] = "\n    replaces = %s\n" % repr(self.migration.replaces) |             items['replaces_str'] = "\n    replaces = %s\n" % repr(self.migration.replaces) | ||||||
|  |  | ||||||
|         return (MIGRATION_TEMPLATE % items).encode("utf8") |         return (MIGRATION_TEMPLATE % items).encode("utf8") | ||||||
|  |  | ||||||
|     @property |     @property | ||||||
| @@ -110,16 +164,16 @@ class MigrationWriter(object): | |||||||
|         else: |         else: | ||||||
|             imports = set(["import %s" % module]) |             imports = set(["import %s" % module]) | ||||||
|             name = path |             name = path | ||||||
|         arg_strings = [] |         strings = [] | ||||||
|         for arg in args: |         for arg in args: | ||||||
|             arg_string, arg_imports = cls.serialize(arg) |             arg_string, arg_imports = cls.serialize(arg) | ||||||
|             arg_strings.append(arg_string) |             strings.append(arg_string) | ||||||
|             imports.update(arg_imports) |             imports.update(arg_imports) | ||||||
|         for kw, arg in kwargs.items(): |         for kw, arg in kwargs.items(): | ||||||
|             arg_string, arg_imports = cls.serialize(arg) |             arg_string, arg_imports = cls.serialize(arg) | ||||||
|             imports.update(arg_imports) |             imports.update(arg_imports) | ||||||
|             arg_strings.append("%s=%s" % (kw, arg_string)) |             strings.append("%s=%s" % (kw, arg_string)) | ||||||
|         return "%s(%s)" % (name, ", ".join(arg_strings)), imports |         return "%s(%s)" % (name, ", ".join(strings)), imports | ||||||
|  |  | ||||||
|     @classmethod |     @classmethod | ||||||
|     def serialize(cls, value): |     def serialize(cls, value): | ||||||
| @@ -140,7 +194,7 @@ class MigrationWriter(object): | |||||||
|             if isinstance(value, set): |             if isinstance(value, set): | ||||||
|                 format = "set([%s])" |                 format = "set([%s])" | ||||||
|             elif isinstance(value, tuple): |             elif isinstance(value, tuple): | ||||||
|                 format = "(%s,)" |                 format = "(%s)" if len(value) else "(%s,)" | ||||||
|             else: |             else: | ||||||
|                 format = "[%s]" |                 format = "[%s]" | ||||||
|             return format % (", ".join(strings)), imports |             return format % (", ".join(strings)), imports | ||||||
| @@ -204,13 +258,18 @@ class MigrationWriter(object): | |||||||
|             raise ValueError("Cannot serialize: %r" % value) |             raise ValueError("Cannot serialize: %r" % value) | ||||||
|  |  | ||||||
|  |  | ||||||
| MIGRATION_TEMPLATE = """# encoding: utf8 | MIGRATION_TEMPLATE = """\ | ||||||
|  | # encoding: utf8 | ||||||
| from django.db import models, migrations | from django.db import models, migrations | ||||||
| %(imports)s | %(imports)s | ||||||
|  |  | ||||||
| class Migration(migrations.Migration): | class Migration(migrations.Migration): | ||||||
|     %(replaces_str)s |     %(replaces_str)s | ||||||
|     dependencies = %(dependencies)s |     dependencies = [ | ||||||
|  | %(dependencies)s\ | ||||||
|  |     ] | ||||||
|  |  | ||||||
|     operations = %(operations)s |     operations = [ | ||||||
|  | %(operations)s\ | ||||||
|  |     ] | ||||||
| """ | """ | ||||||
|   | |||||||
| @@ -107,10 +107,23 @@ class WriterTests(TestCase): | |||||||
|         """ |         """ | ||||||
|         Tests serializing a simple migration. |         Tests serializing a simple migration. | ||||||
|         """ |         """ | ||||||
|  |         fields = { | ||||||
|  |             'charfield': models.DateTimeField(default=datetime.datetime.utcnow), | ||||||
|  |             'datetimefield': models.DateTimeField(default=datetime.datetime.utcnow), | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         options = { | ||||||
|  |             'verbose_name': 'My model', | ||||||
|  |             'verbose_name_plural': 'My models', | ||||||
|  |         } | ||||||
|  |  | ||||||
|         migration = type(str("Migration"), (migrations.Migration,), { |         migration = type(str("Migration"), (migrations.Migration,), { | ||||||
|             "operations": [ |             "operations": [ | ||||||
|  |                 migrations.CreateModel("MyModel", tuple(fields.items()), options, (models.Model,)), | ||||||
|  |                 migrations.CreateModel("MyModel2", tuple(fields.items()), bases=(models.Model,)), | ||||||
|  |                 migrations.CreateModel(name="MyModel3", fields=tuple(fields.items()), options=options, bases=(models.Model,)), | ||||||
|                 migrations.DeleteModel("MyModel"), |                 migrations.DeleteModel("MyModel"), | ||||||
|                 migrations.AddField("OtherModel", "field_name", models.DateTimeField(default=datetime.datetime.utcnow)) |                 migrations.AddField("OtherModel", "datetimefield", fields["datetimefield"]), | ||||||
|             ], |             ], | ||||||
|             "dependencies": [("testapp", "some_other_one")], |             "dependencies": [("testapp", "some_other_one")], | ||||||
|         }) |         }) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user