diff --git a/django/db/migrations/writer.py b/django/db/migrations/writer.py index 692beef384..f36db323a0 100644 --- a/django/db/migrations/writer.py +++ b/django/db/migrations/writer.py @@ -19,7 +19,7 @@ from django.db.migrations.utils import COMPILED_REGEX_TYPE, RegexObject from django.utils import datetime_safe, six from django.utils._os import upath from django.utils.encoding import force_text -from django.utils.functional import Promise +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 @@ -350,6 +350,10 @@ class MigrationWriter(object): # 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)): diff --git a/docs/releases/1.9.txt b/docs/releases/1.9.txt index 7980579a3a..7929bc969e 100644 --- a/docs/releases/1.9.txt +++ b/docs/releases/1.9.txt @@ -451,7 +451,8 @@ Migrations :djadminopt:`migrate --fake-initial <--fake-initial>` to more easily detect initial migrations. -* Added support for serialization of ``functools.partial`` objects. +* Added support for serialization of ``functools.partial`` and ``LazyObject`` + instances. * When supplying ``None`` as a value in :setting:`MIGRATION_MODULES`, Django will consider the app an app without migrations. diff --git a/docs/topics/migrations.txt b/docs/topics/migrations.txt index 44b3afe5f0..24491c6632 100644 --- a/docs/topics/migrations.txt +++ b/docs/topics/migrations.txt @@ -648,6 +648,7 @@ Django can serialize the following: - ``enum.Enum`` instances - ``functools.partial`` instances which have serializable ``func``, ``args``, and ``keywords`` values. +- ``LazyObject`` instances which wrap a serializable value. - Any Django field - Any function or method reference (e.g. ``datetime.datetime.today``) (must be in module's top-level scope) - Any class reference (must be in module's top-level scope) @@ -655,7 +656,8 @@ Django can serialize the following: .. versionchanged:: 1.9 - Serialization support for `functools.partial` was added. + Serialization support for ``functools.partial`` and ``LazyObject`` + instances was added. .. versionchanged:: 1.10 diff --git a/tests/migrations/test_writer.py b/tests/migrations/test_writer.py index e6928b6bbb..fb08a949c8 100644 --- a/tests/migrations/test_writer.py +++ b/tests/migrations/test_writer.py @@ -23,6 +23,7 @@ from django.test import SimpleTestCase, ignore_warnings, mock from django.utils import datetime_safe, six from django.utils._os import upath from django.utils.deconstruct import deconstructible +from django.utils.functional import SimpleLazyObject from django.utils.timezone import FixedOffset, get_default_timezone, utc from django.utils.translation import ugettext_lazy as _ @@ -234,6 +235,11 @@ class WriterTests(SimpleTestCase): ("[list, tuple, dict, set, frozenset]", set()) ) + def test_serialize_lazy_objects(self): + pattern = re.compile(r'^foo$', re.UNICODE) + lazy_pattern = SimpleLazyObject(lambda: pattern) + self.assertEqual(self.serialize_round_trip(lazy_pattern), pattern) + @unittest.skipUnless(enum, "enum34 is required on Python 2") def test_serialize_enums(self): class TextEnum(enum.Enum):