Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Merge pull request #1681 from loic/migrations.format

Fixed #21323 -- Improved readability of serialized Operation.
  • Loading branch information...
commit e8d4aed3b97493a050f69dca1f6562991d5c511b 2 parents 1ea96ac + 374faa4
@andrewgodwin andrewgodwin authored
View
2  django/db/migrations/operations/base.py
@@ -21,6 +21,8 @@ class Operation(object):
# Can this migration be represented as SQL? (things like RunPython cannot)
reduces_to_sql = True
+ serialization_expand_args = []
+
def __new__(cls, *args, **kwargs):
# We capture the arguments to make returning them trivial
self = object.__new__(cls)
View
6 django/db/migrations/operations/models.py
@@ -1,8 +1,8 @@
-from .base import Operation
-from django.utils import six
from django.db import models, router
from django.db.models.options import normalize_unique_together
from django.db.migrations.state import ModelState
+from django.db.migrations.operations.base import Operation
+from django.utils import six
class CreateModel(Operation):
@@ -10,6 +10,8 @@ class CreateModel(Operation):
Create a model's table.
"""
+ serialization_expand_args = ['fields', 'options']
+
def __init__(self, name, fields, options=None, bases=None):
self.name = name
self.fields = fields
View
117 django/db/migrations/writer.py
@@ -1,6 +1,7 @@
from __future__ import unicode_literals
import datetime
+import inspect
from importlib import import_module
import os
import types
@@ -27,6 +28,64 @@ def __init__(self, value, 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):
"""
Takes a Migration instance and is able to produce the contents
@@ -43,40 +102,35 @@ def as_string(self):
items = {
"replaces_str": "",
}
+
imports = set()
+
# Deconstruct operations
- operation_strings = []
+ operations = []
for operation in self.migration.operations:
- name, args, kwargs = operation.deconstruct()
- arg_strings = []
- for arg in args:
- arg_string, arg_imports = self.serialize(arg)
- 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)
+ operation_string, operation_imports = OperationWriter(operation).serialize()
+ imports.update(operation_imports)
+ operations.append(operation_string)
+ items["operations"] = "\n".join(operations) + "\n" if operations else ""
+
# Format dependencies and write out swappable dependencies right
- items["dependencies"] = "["
+ dependencies = []
for dependency in self.migration.dependencies:
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")
else:
- items["dependencies"] += "\n %s," % repr(dependency)
- items["dependencies"] += "\n ]"
+ dependencies.append(" %s," % repr(dependency))
+ items["dependencies"] = "\n".join(dependencies) + "\n" if dependencies else ""
+
# Format imports nicely
imports.discard("from django.db import models")
- if not imports:
- items["imports"] = ""
- else:
- items["imports"] = "\n".join(imports) + "\n"
+ items["imports"] = "\n".join(imports) + "\n" if imports else ""
+
# If there's a replaces, make a string for it
if self.migration.replaces:
items['replaces_str'] = "\n replaces = %s\n" % repr(self.migration.replaces)
+
return (MIGRATION_TEMPLATE % items).encode("utf8")
@property
@@ -110,16 +164,16 @@ def serialize_deconstructed(cls, path, args, kwargs):
else:
imports = set(["import %s" % module])
name = path
- arg_strings = []
+ strings = []
for arg in args:
arg_string, arg_imports = cls.serialize(arg)
- arg_strings.append(arg_string)
+ strings.append(arg_string)
imports.update(arg_imports)
for kw, arg in kwargs.items():
arg_string, arg_imports = cls.serialize(arg)
imports.update(arg_imports)
- arg_strings.append("%s=%s" % (kw, arg_string))
- return "%s(%s)" % (name, ", ".join(arg_strings)), imports
+ strings.append("%s=%s" % (kw, arg_string))
+ return "%s(%s)" % (name, ", ".join(strings)), imports
@classmethod
def serialize(cls, value):
@@ -140,7 +194,7 @@ def serialize(cls, value):
if isinstance(value, set):
format = "set([%s])"
elif isinstance(value, tuple):
- format = "(%s,)"
+ format = "(%s)" if len(value) else "(%s,)"
else:
format = "[%s]"
return format % (", ".join(strings)), imports
@@ -204,13 +258,18 @@ def serialize(cls, value):
raise ValueError("Cannot serialize: %r" % value)
-MIGRATION_TEMPLATE = """# encoding: utf8
+MIGRATION_TEMPLATE = """\
+# encoding: utf8
from django.db import models, migrations
%(imports)s
class Migration(migrations.Migration):
%(replaces_str)s
- dependencies = %(dependencies)s
+ dependencies = [
+%(dependencies)s\
+ ]
- operations = %(operations)s
+ operations = [
+%(operations)s\
+ ]
"""
View
15 tests/migrations/test_writer.py
@@ -107,10 +107,23 @@ def test_simple_migration(self):
"""
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,), {
"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.AddField("OtherModel", "field_name", models.DateTimeField(default=datetime.datetime.utcnow))
+ migrations.AddField("OtherModel", "datetimefield", fields["datetimefield"]),
],
"dependencies": [("testapp", "some_other_one")],
})
Please sign in to comment.
Something went wrong with that request. Please try again.