mirror of
				https://github.com/django/django.git
				synced 2025-10-31 09:41:08 +00:00 
			
		
		
		
	Fixed #26151 -- Refactored MigrationWriter.serialize()
Thanks Markus Holtermann for review.
This commit is contained in:
		
				
					committed by
					
						 Tim Graham
						Tim Graham
					
				
			
			
				
	
			
			
			
						parent
						
							fc584f0685
						
					
				
				
					commit
					4b1529e2cb
				
			
							
								
								
									
										388
									
								
								django/db/migrations/serializer.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										388
									
								
								django/db/migrations/serializer.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,388 @@ | ||||
| from __future__ import unicode_literals | ||||
|  | ||||
| import collections | ||||
| import datetime | ||||
| import decimal | ||||
| import functools | ||||
| import math | ||||
| import types | ||||
| from importlib import import_module | ||||
|  | ||||
| from django.db import models | ||||
| from django.db.migrations.operations.base import Operation | ||||
| from django.db.migrations.utils import COMPILED_REGEX_TYPE, RegexObject | ||||
| from django.utils import datetime_safe, six | ||||
| from django.utils.encoding import force_text | ||||
| from django.utils.functional import LazyObject, Promise | ||||
| from django.utils.timezone import utc | ||||
| from django.utils.version import get_docs_version | ||||
|  | ||||
| try: | ||||
|     import enum | ||||
| except ImportError: | ||||
|     # No support on Python 2 if enum34 isn't installed. | ||||
|     enum = None | ||||
|  | ||||
|  | ||||
| class BaseSerializer(object): | ||||
|     def __init__(self, value): | ||||
|         self.value = value | ||||
|  | ||||
|     def serialize(self): | ||||
|         raise NotImplementedError('Subclasses of BaseSerializer must implement the serialize() method.') | ||||
|  | ||||
|  | ||||
| class BaseSequenceSerializer(BaseSerializer): | ||||
|     def _format(self): | ||||
|         raise NotImplementedError('Subclasses of BaseSequenceSerializer must implement the _format() method.') | ||||
|  | ||||
|     def serialize(self): | ||||
|         imports = set() | ||||
|         strings = [] | ||||
|         for item in self.value: | ||||
|             item_string, item_imports = serializer_factory(item).serialize() | ||||
|             imports.update(item_imports) | ||||
|             strings.append(item_string) | ||||
|         value = self._format() | ||||
|         return value % (", ".join(strings)), imports | ||||
|  | ||||
|  | ||||
| class BaseSimpleSerializer(BaseSerializer): | ||||
|     def serialize(self): | ||||
|         return repr(self.value), set() | ||||
|  | ||||
|  | ||||
| class ByteTypeSerializer(BaseSerializer): | ||||
|     def serialize(self): | ||||
|         value_repr = repr(self.value) | ||||
|         if six.PY2: | ||||
|             # Prepend the `b` prefix since we're importing unicode_literals | ||||
|             value_repr = 'b' + value_repr | ||||
|         return value_repr, set() | ||||
|  | ||||
|  | ||||
| class DatetimeSerializer(BaseSerializer): | ||||
|     def serialize(self): | ||||
|         if self.value.tzinfo is not None and self.value.tzinfo != utc: | ||||
|             self.value = self.value.astimezone(utc) | ||||
|         value_repr = repr(self.value).replace("<UTC>", "utc") | ||||
|         if isinstance(self.value, datetime_safe.datetime): | ||||
|             value_repr = "datetime.%s" % value_repr | ||||
|         imports = ["import datetime"] | ||||
|         if self.value.tzinfo is not None: | ||||
|             imports.append("from django.utils.timezone import utc") | ||||
|         return value_repr, set(imports) | ||||
|  | ||||
|  | ||||
| class DateSerializer(BaseSerializer): | ||||
|     def serialize(self): | ||||
|         value_repr = repr(self.value) | ||||
|         if isinstance(self.value, datetime_safe.date): | ||||
|             value_repr = "datetime.%s" % value_repr | ||||
|         return value_repr, {"import datetime"} | ||||
|  | ||||
|  | ||||
| class DecimalSerializer(BaseSerializer): | ||||
|     def serialize(self): | ||||
|         return repr(self.value), {"from decimal import Decimal"} | ||||
|  | ||||
|  | ||||
| class DeconstructableSerializer(BaseSerializer): | ||||
|     @staticmethod | ||||
|     def serialize_deconstructed(path, args, kwargs): | ||||
|         name, imports = DeconstructableSerializer._serialize_path(path) | ||||
|         strings = [] | ||||
|         for arg in args: | ||||
|             arg_string, arg_imports = serializer_factory(arg).serialize() | ||||
|             strings.append(arg_string) | ||||
|             imports.update(arg_imports) | ||||
|         for kw, arg in sorted(kwargs.items()): | ||||
|             arg_string, arg_imports = serializer_factory(arg).serialize() | ||||
|             imports.update(arg_imports) | ||||
|             strings.append("%s=%s" % (kw, arg_string)) | ||||
|         return "%s(%s)" % (name, ", ".join(strings)), imports | ||||
|  | ||||
|     @staticmethod | ||||
|     def _serialize_path(path): | ||||
|         module, name = path.rsplit(".", 1) | ||||
|         if module == "django.db.models": | ||||
|             imports = {"from django.db import models"} | ||||
|             name = "models.%s" % name | ||||
|         else: | ||||
|             imports = {"import %s" % module} | ||||
|             name = path | ||||
|         return name, imports | ||||
|  | ||||
|     def serialize(self): | ||||
|         return self.serialize_deconstructed(*self.value.deconstruct()) | ||||
|  | ||||
|  | ||||
| class DictionarySerializer(BaseSerializer): | ||||
|     def serialize(self): | ||||
|         imports = set() | ||||
|         strings = [] | ||||
|         for k, v in sorted(self.value.items()): | ||||
|             k_string, k_imports = serializer_factory(k).serialize() | ||||
|             v_string, v_imports = serializer_factory(v).serialize() | ||||
|             imports.update(k_imports) | ||||
|             imports.update(v_imports) | ||||
|             strings.append((k_string, v_string)) | ||||
|         return "{%s}" % (", ".join("%s: %s" % (k, v) for k, v in strings)), imports | ||||
|  | ||||
|  | ||||
| class EnumSerializer(BaseSerializer): | ||||
|     def serialize(self): | ||||
|         enum_class = self.value.__class__ | ||||
|         module = enum_class.__module__ | ||||
|         imports = {"import %s" % module} | ||||
|         v_string, v_imports = serializer_factory(self.value.value).serialize() | ||||
|         imports.update(v_imports) | ||||
|         return "%s.%s(%s)" % (module, enum_class.__name__, v_string), imports | ||||
|  | ||||
|  | ||||
| class FloatSerializer(BaseSimpleSerializer): | ||||
|     def serialize(self): | ||||
|         if math.isnan(self.value) or math.isinf(self.value): | ||||
|             return 'float("{}")'.format(self.value), set() | ||||
|         return super(FloatSerializer, self).serialize() | ||||
|  | ||||
|  | ||||
| class FrozensetSerializer(BaseSequenceSerializer): | ||||
|     def _format(self): | ||||
|         return "frozenset([%s])" | ||||
|  | ||||
|  | ||||
| class FunctionTypeSerializer(BaseSerializer): | ||||
|     def serialize(self): | ||||
|         if getattr(self.value, "__self__", None) and isinstance(self.value.__self__, type): | ||||
|             klass = self.value.__self__ | ||||
|             module = klass.__module__ | ||||
|             return "%s.%s.%s" % (module, klass.__name__, self.value.__name__), {"import %s" % module} | ||||
|         # Further error checking | ||||
|         if self.value.__name__ == '<lambda>': | ||||
|             raise ValueError("Cannot serialize function: lambda") | ||||
|         if self.value.__module__ is None: | ||||
|             raise ValueError("Cannot serialize function %r: No module" % self.value) | ||||
|         # Python 3 is a lot easier, and only uses this branch if it's not local. | ||||
|         if getattr(self.value, "__qualname__", None) and getattr(self.value, "__module__", None): | ||||
|             if "<" not in self.value.__qualname__:  # Qualname can include <locals> | ||||
|                 return "%s.%s" % \ | ||||
|                     (self.value.__module__, self.value.__qualname__), {"import %s" % self.value.__module__} | ||||
|         # Python 2/fallback version | ||||
|         module_name = self.value.__module__ | ||||
|         # Make sure it's actually there and not an unbound method | ||||
|         module = import_module(module_name) | ||||
|         if not hasattr(module, self.value.__name__): | ||||
|             raise ValueError( | ||||
|                 "Could not find function %s in %s.\n" | ||||
|                 "Please note that due to Python 2 limitations, you cannot " | ||||
|                 "serialize unbound method functions (e.g. a method " | ||||
|                 "declared and used in the same class body). Please move " | ||||
|                 "the function into the main module body to use migrations.\n" | ||||
|                 "For more information, see " | ||||
|                 "https://docs.djangoproject.com/en/%s/topics/migrations/#serializing-values" | ||||
|                 % (self.value.__name__, module_name, get_docs_version()) | ||||
|             ) | ||||
|         # Needed on Python 2 only | ||||
|         if module_name == '__builtin__': | ||||
|             return self.value.__name__, set() | ||||
|         return "%s.%s" % (module_name, self.value.__name__), {"import %s" % module_name} | ||||
|  | ||||
|  | ||||
| class FunctoolsPartialSerializer(BaseSerializer): | ||||
|     def serialize(self): | ||||
|         imports = {'import functools'} | ||||
|         # Serialize functools.partial() arguments | ||||
|         func_string, func_imports = serializer_factory(self.value.func).serialize() | ||||
|         args_string, args_imports = serializer_factory(self.value.args).serialize() | ||||
|         keywords_string, keywords_imports = serializer_factory(self.value.keywords).serialize() | ||||
|         # Add any imports needed by arguments | ||||
|         imports.update(func_imports) | ||||
|         imports.update(args_imports) | ||||
|         imports.update(keywords_imports) | ||||
|         return ( | ||||
|             "functools.partial(%s, *%s, **%s)" % ( | ||||
|                 func_string, args_string, keywords_string, | ||||
|             ), | ||||
|             imports, | ||||
|         ) | ||||
|  | ||||
|  | ||||
| class IterableSerializer(BaseSerializer): | ||||
|     def serialize(self): | ||||
|         imports = set() | ||||
|         strings = [] | ||||
|         for item in self.value: | ||||
|             item_string, item_imports = serializer_factory(item).serialize() | ||||
|             imports.update(item_imports) | ||||
|             strings.append(item_string) | ||||
|         # When len(strings)==0, the empty iterable should be serialized as | ||||
|         # "()", not "(,)" because (,) is invalid Python syntax. | ||||
|         value = "(%s)" if len(strings) != 1 else "(%s,)" | ||||
|         return value % (", ".join(strings)), imports | ||||
|  | ||||
|  | ||||
| class ModelFieldSerializer(DeconstructableSerializer): | ||||
|     def serialize(self): | ||||
|         attr_name, path, args, kwargs = self.value.deconstruct() | ||||
|         return self.serialize_deconstructed(path, args, kwargs) | ||||
|  | ||||
|  | ||||
| class ModelManagerSerializer(DeconstructableSerializer): | ||||
|     def serialize(self): | ||||
|         as_manager, manager_path, qs_path, args, kwargs = self.value.deconstruct() | ||||
|         if as_manager: | ||||
|             name, imports = self._serialize_path(qs_path) | ||||
|             return "%s.as_manager()" % name, imports | ||||
|         else: | ||||
|             return self.serialize_deconstructed(manager_path, args, kwargs) | ||||
|  | ||||
|  | ||||
| class OperationSerializer(BaseSerializer): | ||||
|     def serialize(self): | ||||
|         from django.db.migrations.writer import OperationWriter | ||||
|         string, imports = OperationWriter(self.value, indentation=0).serialize() | ||||
|         # Nested operation, trailing comma is handled in upper OperationWriter._write() | ||||
|         return string.rstrip(','), imports | ||||
|  | ||||
|  | ||||
| class RegexSerializer(BaseSerializer): | ||||
|     def serialize(self): | ||||
|         imports = {"import re"} | ||||
|         regex_pattern, pattern_imports = serializer_factory(self.value.pattern).serialize() | ||||
|         regex_flags, flag_imports = serializer_factory(self.value.flags).serialize() | ||||
|         imports.update(pattern_imports) | ||||
|         imports.update(flag_imports) | ||||
|         args = [regex_pattern] | ||||
|         if self.value.flags: | ||||
|             args.append(regex_flags) | ||||
|         return "re.compile(%s)" % ', '.join(args), imports | ||||
|  | ||||
|  | ||||
| class SequenceSerializer(BaseSequenceSerializer): | ||||
|     def _format(self): | ||||
|         return "[%s]" | ||||
|  | ||||
|  | ||||
| class SetSerializer(BaseSequenceSerializer): | ||||
|     def _format(self): | ||||
|         # Don't use the literal "{%s}" as it doesn't support empty set | ||||
|         return "set([%s])" | ||||
|  | ||||
|  | ||||
| class SettingsReferenceSerializer(BaseSerializer): | ||||
|     def serialize(self): | ||||
|         return "settings.%s" % self.value.setting_name, {"from django.conf import settings"} | ||||
|  | ||||
|  | ||||
| class TextTypeSerializer(BaseSerializer): | ||||
|     def serialize(self): | ||||
|         value_repr = repr(self.value) | ||||
|         if six.PY2: | ||||
|             # Strip the `u` prefix since we're importing unicode_literals | ||||
|             value_repr = value_repr[1:] | ||||
|         return value_repr, set() | ||||
|  | ||||
|  | ||||
| class TimedeltaSerializer(BaseSerializer): | ||||
|     def serialize(self): | ||||
|         return repr(self.value), {"import datetime"} | ||||
|  | ||||
|  | ||||
| class TimeSerializer(BaseSerializer): | ||||
|     def serialize(self): | ||||
|         value_repr = repr(self.value) | ||||
|         if isinstance(self.value, datetime_safe.time): | ||||
|             value_repr = "datetime.%s" % value_repr | ||||
|         return value_repr, {"import datetime"} | ||||
|  | ||||
|  | ||||
| class TupleSerializer(BaseSequenceSerializer): | ||||
|     def _format(self): | ||||
|         # When len(value)==0, the empty tuple should be serialized as "()", | ||||
|         # not "(,)" because (,) is invalid Python syntax. | ||||
|         return "(%s)" if len(self.value) != 1 else "(%s,)" | ||||
|  | ||||
|  | ||||
| class TypeSerializer(BaseSerializer): | ||||
|     def serialize(self): | ||||
|         special_cases = [ | ||||
|             (models.Model, "models.Model", []), | ||||
|         ] | ||||
|         for case, string, imports in special_cases: | ||||
|             if case is self.value: | ||||
|                 return string, set(imports) | ||||
|         if hasattr(self.value, "__module__"): | ||||
|             module = self.value.__module__ | ||||
|             if module == six.moves.builtins.__name__: | ||||
|                 return self.value.__name__, set() | ||||
|             else: | ||||
|                 return "%s.%s" % (module, self.value.__name__), {"import %s" % module} | ||||
|  | ||||
|  | ||||
| def serializer_factory(value): | ||||
|     from django.db.migrations.writer import SettingsReference | ||||
|     if isinstance(value, Promise): | ||||
|         value = force_text(value) | ||||
|     elif isinstance(value, LazyObject): | ||||
|         # The unwrapped value is returned as the first item of the arguments | ||||
|         # tuple. | ||||
|         value = value.__reduce__()[1][0] | ||||
|  | ||||
|     # Unfortunately some of these are order-dependent. | ||||
|     if isinstance(value, frozenset): | ||||
|         return FrozensetSerializer(value) | ||||
|     if isinstance(value, list): | ||||
|         return SequenceSerializer(value) | ||||
|     if isinstance(value, set): | ||||
|         return SetSerializer(value) | ||||
|     if isinstance(value, tuple): | ||||
|         return TupleSerializer(value) | ||||
|     if isinstance(value, dict): | ||||
|         return DictionarySerializer(value) | ||||
|     if enum and isinstance(value, enum.Enum): | ||||
|         return EnumSerializer(value) | ||||
|     if isinstance(value, datetime.datetime): | ||||
|         return DatetimeSerializer(value) | ||||
|     if isinstance(value, datetime.date): | ||||
|         return DateSerializer(value) | ||||
|     if isinstance(value, datetime.time): | ||||
|         return TimeSerializer(value) | ||||
|     if isinstance(value, datetime.timedelta): | ||||
|         return TimedeltaSerializer(value) | ||||
|     if isinstance(value, SettingsReference): | ||||
|         return SettingsReferenceSerializer(value) | ||||
|     if isinstance(value, float): | ||||
|         return FloatSerializer(value) | ||||
|     if isinstance(value, six.integer_types + (bool, type(None))): | ||||
|         return BaseSimpleSerializer(value) | ||||
|     if isinstance(value, six.binary_type): | ||||
|         return ByteTypeSerializer(value) | ||||
|     if isinstance(value, six.text_type): | ||||
|         return TextTypeSerializer(value) | ||||
|     if isinstance(value, decimal.Decimal): | ||||
|         return DecimalSerializer(value) | ||||
|     if isinstance(value, models.Field): | ||||
|         return ModelFieldSerializer(value) | ||||
|     if isinstance(value, type): | ||||
|         return TypeSerializer(value) | ||||
|     if isinstance(value, models.manager.BaseManager): | ||||
|         return ModelManagerSerializer(value) | ||||
|     if isinstance(value, Operation): | ||||
|         return OperationSerializer(value) | ||||
|     if isinstance(value, functools.partial): | ||||
|         return FunctoolsPartialSerializer(value) | ||||
|     # Anything that knows how to deconstruct itself. | ||||
|     if hasattr(value, 'deconstruct'): | ||||
|         return DeconstructableSerializer(value) | ||||
|     if isinstance(value, (types.FunctionType, types.BuiltinFunctionType)): | ||||
|         return FunctionTypeSerializer(value) | ||||
|     if isinstance(value, collections.Iterable): | ||||
|         return IterableSerializer(value) | ||||
|     if isinstance(value, (COMPILED_REGEX_TYPE, RegexObject)): | ||||
|         return RegexSerializer(value) | ||||
|     raise ValueError( | ||||
|         "Cannot serialize: %r\nThere are some values Django cannot serialize into " | ||||
|         "migration files.\nFor more, see https://docs.djangoproject.com/en/%s/" | ||||
|         "topics/migrations/#migration-serializing" % (value, get_docs_version()) | ||||
|     ) | ||||
| @@ -1,29 +1,19 @@ | ||||
| from __future__ import unicode_literals | ||||
|  | ||||
| import collections | ||||
| import datetime | ||||
| import decimal | ||||
| import functools | ||||
| import math | ||||
| import os | ||||
| import re | ||||
| import types | ||||
| from importlib import import_module | ||||
|  | ||||
| from django import get_version | ||||
| from django.apps import apps | ||||
| from django.db import migrations, models | ||||
| from django.db import migrations | ||||
| from django.db.migrations.loader import MigrationLoader | ||||
| from django.db.migrations.operations.base import Operation | ||||
| from django.db.migrations.utils import COMPILED_REGEX_TYPE, RegexObject | ||||
| from django.utils import datetime_safe, six | ||||
| from django.db.migrations.serializer import serializer_factory | ||||
| from django.utils._os import upath | ||||
| from django.utils.encoding import force_text | ||||
| from django.utils.functional import LazyObject, Promise | ||||
| from django.utils.inspect import get_func_args | ||||
| from django.utils.module_loading import module_dir | ||||
| from django.utils.timezone import now, utc | ||||
| from django.utils.version import get_docs_version | ||||
| from django.utils.timezone import now | ||||
|  | ||||
| try: | ||||
|     import enum | ||||
| @@ -229,20 +219,6 @@ class MigrationWriter(object): | ||||
|  | ||||
|         return (MIGRATION_TEMPLATE % items).encode("utf8") | ||||
|  | ||||
|     @staticmethod | ||||
|     def serialize_datetime(value): | ||||
|         """ | ||||
|         Returns a serialized version of a datetime object that is valid, | ||||
|         executable python code. It converts timezone-aware values to utc with | ||||
|         an 'executable' utc representation of tzinfo. | ||||
|         """ | ||||
|         if value.tzinfo is not None and value.tzinfo != utc: | ||||
|             value = value.astimezone(utc) | ||||
|         value_repr = repr(value).replace("<UTC>", "utc") | ||||
|         if isinstance(value, datetime_safe.datetime): | ||||
|             value_repr = "datetime.%s" % value_repr | ||||
|         return value_repr | ||||
|  | ||||
|     @property | ||||
|     def basedir(self): | ||||
|         migrations_package_name = MigrationLoader.migrations_module(self.migration.app_label) | ||||
| @@ -312,247 +288,9 @@ class MigrationWriter(object): | ||||
|     def path(self): | ||||
|         return os.path.join(self.basedir, self.filename) | ||||
|  | ||||
|     @classmethod | ||||
|     def serialize_deconstructed(cls, path, args, kwargs): | ||||
|         name, imports = cls._serialize_path(path) | ||||
|         strings = [] | ||||
|         for arg in args: | ||||
|             arg_string, arg_imports = cls.serialize(arg) | ||||
|             strings.append(arg_string) | ||||
|             imports.update(arg_imports) | ||||
|         for kw, arg in sorted(kwargs.items()): | ||||
|             arg_string, arg_imports = cls.serialize(arg) | ||||
|             imports.update(arg_imports) | ||||
|             strings.append("%s=%s" % (kw, arg_string)) | ||||
|         return "%s(%s)" % (name, ", ".join(strings)), imports | ||||
|  | ||||
|     @classmethod | ||||
|     def _serialize_path(cls, path): | ||||
|         module, name = path.rsplit(".", 1) | ||||
|         if module == "django.db.models": | ||||
|             imports = {"from django.db import models"} | ||||
|             name = "models.%s" % name | ||||
|         else: | ||||
|             imports = {"import %s" % module} | ||||
|             name = path | ||||
|         return name, imports | ||||
|  | ||||
|     @classmethod | ||||
|     def serialize(cls, value): | ||||
|         """ | ||||
|         Serializes the value to a string that's parsable by Python, along | ||||
|         with any needed imports to make that string work. | ||||
|         More advanced than repr() as it can encode things | ||||
|         like datetime.datetime.now. | ||||
|         """ | ||||
|         # FIXME: Ideally Promise would be reconstructible, but for now we | ||||
|         # use force_text on them and defer to the normal string serialization | ||||
|         # process. | ||||
|         if isinstance(value, Promise): | ||||
|             value = force_text(value) | ||||
|         elif isinstance(value, LazyObject): | ||||
|             # The unwrapped value is returned as the first item of the | ||||
|             # arguments tuple. | ||||
|             value = value.__reduce__()[1][0] | ||||
|  | ||||
|         # Sequences | ||||
|         if isinstance(value, (frozenset, list, set, tuple)): | ||||
|             imports = set() | ||||
|             strings = [] | ||||
|             for item in value: | ||||
|                 item_string, item_imports = cls.serialize(item) | ||||
|                 imports.update(item_imports) | ||||
|                 strings.append(item_string) | ||||
|             if isinstance(value, set): | ||||
|                 # Don't use the literal "{%s}" as it doesn't support empty set | ||||
|                 format = "set([%s])" | ||||
|             elif isinstance(value, frozenset): | ||||
|                 format = "frozenset([%s])" | ||||
|             elif isinstance(value, tuple): | ||||
|                 # When len(value)==0, the empty tuple should be serialized as | ||||
|                 # "()", not "(,)" because (,) is invalid Python syntax. | ||||
|                 format = "(%s)" if len(value) != 1 else "(%s,)" | ||||
|             else: | ||||
|                 format = "[%s]" | ||||
|             return format % (", ".join(strings)), imports | ||||
|         # Dictionaries | ||||
|         elif isinstance(value, dict): | ||||
|             imports = set() | ||||
|             strings = [] | ||||
|             for k, v in sorted(value.items()): | ||||
|                 k_string, k_imports = cls.serialize(k) | ||||
|                 v_string, v_imports = cls.serialize(v) | ||||
|                 imports.update(k_imports) | ||||
|                 imports.update(v_imports) | ||||
|                 strings.append((k_string, v_string)) | ||||
|             return "{%s}" % (", ".join("%s: %s" % (k, v) for k, v in strings)), imports | ||||
|         # Enums | ||||
|         elif enum and isinstance(value, enum.Enum): | ||||
|             enum_class = value.__class__ | ||||
|             module = enum_class.__module__ | ||||
|             imports = {"import %s" % module} | ||||
|             v_string, v_imports = cls.serialize(value.value) | ||||
|             imports.update(v_imports) | ||||
|             return "%s.%s(%s)" % (module, enum_class.__name__, v_string), imports | ||||
|         # Datetimes | ||||
|         elif isinstance(value, datetime.datetime): | ||||
|             value_repr = cls.serialize_datetime(value) | ||||
|             imports = ["import datetime"] | ||||
|             if value.tzinfo is not None: | ||||
|                 imports.append("from django.utils.timezone import utc") | ||||
|             return value_repr, set(imports) | ||||
|         # Dates | ||||
|         elif isinstance(value, datetime.date): | ||||
|             value_repr = repr(value) | ||||
|             if isinstance(value, datetime_safe.date): | ||||
|                 value_repr = "datetime.%s" % value_repr | ||||
|             return value_repr, {"import datetime"} | ||||
|         # Times | ||||
|         elif isinstance(value, datetime.time): | ||||
|             value_repr = repr(value) | ||||
|             if isinstance(value, datetime_safe.time): | ||||
|                 value_repr = "datetime.%s" % value_repr | ||||
|             return value_repr, {"import datetime"} | ||||
|         # Timedeltas | ||||
|         elif isinstance(value, datetime.timedelta): | ||||
|             return repr(value), {"import datetime"} | ||||
|         # Settings references | ||||
|         elif isinstance(value, SettingsReference): | ||||
|             return "settings.%s" % value.setting_name, {"from django.conf import settings"} | ||||
|         # Simple types | ||||
|         elif isinstance(value, float): | ||||
|             if math.isnan(value) or math.isinf(value): | ||||
|                 return 'float("{}")'.format(value), set() | ||||
|             return repr(value), set() | ||||
|         elif isinstance(value, six.integer_types + (bool, type(None))): | ||||
|             return repr(value), set() | ||||
|         elif isinstance(value, six.binary_type): | ||||
|             value_repr = repr(value) | ||||
|             if six.PY2: | ||||
|                 # Prepend the `b` prefix since we're importing unicode_literals | ||||
|                 value_repr = 'b' + value_repr | ||||
|             return value_repr, set() | ||||
|         elif isinstance(value, six.text_type): | ||||
|             value_repr = repr(value) | ||||
|             if six.PY2: | ||||
|                 # Strip the `u` prefix since we're importing unicode_literals | ||||
|                 value_repr = value_repr[1:] | ||||
|             return value_repr, set() | ||||
|         # Decimal | ||||
|         elif isinstance(value, decimal.Decimal): | ||||
|             return repr(value), {"from decimal import Decimal"} | ||||
|         # Django fields | ||||
|         elif isinstance(value, models.Field): | ||||
|             attr_name, path, args, kwargs = value.deconstruct() | ||||
|             return cls.serialize_deconstructed(path, args, kwargs) | ||||
|         # Classes | ||||
|         elif isinstance(value, type): | ||||
|             special_cases = [ | ||||
|                 (models.Model, "models.Model", []), | ||||
|             ] | ||||
|             for case, string, imports in special_cases: | ||||
|                 if case is value: | ||||
|                     return string, set(imports) | ||||
|             if hasattr(value, "__module__"): | ||||
|                 module = value.__module__ | ||||
|                 if module == six.moves.builtins.__name__: | ||||
|                     return value.__name__, set() | ||||
|                 else: | ||||
|                     return "%s.%s" % (module, value.__name__), {"import %s" % module} | ||||
|         elif isinstance(value, models.manager.BaseManager): | ||||
|             as_manager, manager_path, qs_path, args, kwargs = value.deconstruct() | ||||
|             if as_manager: | ||||
|                 name, imports = cls._serialize_path(qs_path) | ||||
|                 return "%s.as_manager()" % name, imports | ||||
|             else: | ||||
|                 return cls.serialize_deconstructed(manager_path, args, kwargs) | ||||
|         elif isinstance(value, Operation): | ||||
|             string, imports = OperationWriter(value, indentation=0).serialize() | ||||
|             # Nested operation, trailing comma is handled in upper OperationWriter._write() | ||||
|             return string.rstrip(','), imports | ||||
|         elif isinstance(value, functools.partial): | ||||
|             imports = {'import functools'} | ||||
|             # Serialize functools.partial() arguments | ||||
|             func_string, func_imports = cls.serialize(value.func) | ||||
|             args_string, args_imports = cls.serialize(value.args) | ||||
|             keywords_string, keywords_imports = cls.serialize(value.keywords) | ||||
|             # Add any imports needed by arguments | ||||
|             imports.update(func_imports) | ||||
|             imports.update(args_imports) | ||||
|             imports.update(keywords_imports) | ||||
|             return ( | ||||
|                 "functools.partial(%s, *%s, **%s)" % ( | ||||
|                     func_string, args_string, keywords_string, | ||||
|                 ), | ||||
|                 imports, | ||||
|             ) | ||||
|         # Anything that knows how to deconstruct itself. | ||||
|         elif hasattr(value, 'deconstruct'): | ||||
|             return cls.serialize_deconstructed(*value.deconstruct()) | ||||
|         # Functions | ||||
|         elif isinstance(value, (types.FunctionType, types.BuiltinFunctionType)): | ||||
|             # @classmethod? | ||||
|             if getattr(value, "__self__", None) and isinstance(value.__self__, type): | ||||
|                 klass = value.__self__ | ||||
|                 module = klass.__module__ | ||||
|                 return "%s.%s.%s" % (module, klass.__name__, value.__name__), {"import %s" % module} | ||||
|             # Further error checking | ||||
|             if value.__name__ == '<lambda>': | ||||
|                 raise ValueError("Cannot serialize function: lambda") | ||||
|             if value.__module__ is None: | ||||
|                 raise ValueError("Cannot serialize function %r: No module" % value) | ||||
|             # Python 3 is a lot easier, and only uses this branch if it's not local. | ||||
|             if getattr(value, "__qualname__", None) and getattr(value, "__module__", None): | ||||
|                 if "<" not in value.__qualname__:  # Qualname can include <locals> | ||||
|                     return "%s.%s" % (value.__module__, value.__qualname__), {"import %s" % value.__module__} | ||||
|             # Python 2/fallback version | ||||
|             module_name = value.__module__ | ||||
|             # Make sure it's actually there and not an unbound method | ||||
|             module = import_module(module_name) | ||||
|             if not hasattr(module, value.__name__): | ||||
|                 raise ValueError( | ||||
|                     "Could not find function %s in %s.\n" | ||||
|                     "Please note that due to Python 2 limitations, you cannot " | ||||
|                     "serialize unbound method functions (e.g. a method " | ||||
|                     "declared and used in the same class body). Please move " | ||||
|                     "the function into the main module body to use migrations.\n" | ||||
|                     "For more information, see " | ||||
|                     "https://docs.djangoproject.com/en/%s/topics/migrations/#serializing-values" | ||||
|                     % (value.__name__, module_name, get_docs_version())) | ||||
|             # Needed on Python 2 only | ||||
|             if module_name == '__builtin__': | ||||
|                 return value.__name__, set() | ||||
|             return "%s.%s" % (module_name, value.__name__), {"import %s" % module_name} | ||||
|         # Other iterables | ||||
|         elif isinstance(value, collections.Iterable): | ||||
|             imports = set() | ||||
|             strings = [] | ||||
|             for item in value: | ||||
|                 item_string, item_imports = cls.serialize(item) | ||||
|                 imports.update(item_imports) | ||||
|                 strings.append(item_string) | ||||
|             # When len(strings)==0, the empty iterable should be serialized as | ||||
|             # "()", not "(,)" because (,) is invalid Python syntax. | ||||
|             format = "(%s)" if len(strings) != 1 else "(%s,)" | ||||
|             return format % (", ".join(strings)), imports | ||||
|         # Compiled regex | ||||
|         elif isinstance(value, (COMPILED_REGEX_TYPE, RegexObject)): | ||||
|             imports = {"import re"} | ||||
|             regex_pattern, pattern_imports = cls.serialize(value.pattern) | ||||
|             regex_flags, flag_imports = cls.serialize(value.flags) | ||||
|             imports.update(pattern_imports) | ||||
|             imports.update(flag_imports) | ||||
|             args = [regex_pattern] | ||||
|             if value.flags: | ||||
|                 args.append(regex_flags) | ||||
|             return "re.compile(%s)" % ', '.join(args), imports | ||||
|         # Uh oh. | ||||
|         else: | ||||
|             raise ValueError( | ||||
|                 "Cannot serialize: %r\nThere are some values Django cannot serialize into " | ||||
|                 "migration files.\nFor more, see https://docs.djangoproject.com/en/%s/" | ||||
|                 "topics/migrations/#migration-serializing" % (value, get_docs_version()) | ||||
|             ) | ||||
|         return serializer_factory(value).serialize() | ||||
|  | ||||
|  | ||||
| MIGRATION_TEMPLATE = """\ | ||||
|   | ||||
		Reference in New Issue
	
	Block a user