diff --git a/django/db/migrations/writer.py b/django/db/migrations/writer.py index aa9f393e4b..f48ac9a7b8 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 @@ -344,6 +344,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 b167500c76..bf6f45b450 100644 --- a/docs/releases/1.9.txt +++ b/docs/releases/1.9.txt @@ -448,7 +448,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 57b6c3216a..24236ec9da 100644 --- a/docs/topics/migrations.txt +++ b/docs/topics/migrations.txt @@ -659,6 +659,7 @@ Django can serialize the following: - ``decimal.Decimal`` 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) @@ -666,7 +667,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. Django can serialize the following on Python 3 only: diff --git a/tests/migrations/test_writer.py b/tests/migrations/test_writer.py index 12df772df5..b3c41565e3 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 _ @@ -229,6 +230,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) + def test_serialize_functions(self): with six.assertRaisesRegex(self, ValueError, 'Cannot serialize function: lambda'): self.assertSerializedEqual(lambda x: 42)