[1.9.x] Fixed #25807 -- Instructed the migration writer about lazy objects.

Thanks to Trac alias mrgaolei for the report, Baptiste for the confirmation
and Tim for the review.

Backport of cc2ca9c550 from master
This commit is contained in:
Simon Charette 2015-11-25 12:31:23 -05:00
parent f7e599ad25
commit b4a1d545db
4 changed files with 16 additions and 3 deletions

View File

@ -19,7 +19,7 @@ from django.db.migrations.utils import COMPILED_REGEX_TYPE, RegexObject
from django.utils import datetime_safe, six from django.utils import datetime_safe, six
from django.utils._os import upath from django.utils._os import upath
from django.utils.encoding import force_text 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.inspect import get_func_args
from django.utils.module_loading import module_dir from django.utils.module_loading import module_dir
from django.utils.timezone import now, utc from django.utils.timezone import now, utc
@ -344,6 +344,10 @@ class MigrationWriter(object):
# process. # process.
if isinstance(value, Promise): if isinstance(value, Promise):
value = force_text(value) 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 # Sequences
if isinstance(value, (frozenset, list, set, tuple)): if isinstance(value, (frozenset, list, set, tuple)):

View File

@ -448,7 +448,8 @@ Migrations
:djadminopt:`migrate --fake-initial <--fake-initial>` to more easily detect :djadminopt:`migrate --fake-initial <--fake-initial>` to more easily detect
initial migrations. 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 * When supplying ``None`` as a value in :setting:`MIGRATION_MODULES`, Django
will consider the app an app without migrations. will consider the app an app without migrations.

View File

@ -659,6 +659,7 @@ Django can serialize the following:
- ``decimal.Decimal`` instances - ``decimal.Decimal`` instances
- ``functools.partial`` instances which have serializable ``func``, ``args``, - ``functools.partial`` instances which have serializable ``func``, ``args``,
and ``keywords`` values. and ``keywords`` values.
- ``LazyObject`` instances which wrap a serializable value.
- Any Django field - Any Django field
- Any function or method reference (e.g. ``datetime.datetime.today``) (must be in module's top-level scope) - 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) - Any class reference (must be in module's top-level scope)
@ -666,7 +667,8 @@ Django can serialize the following:
.. versionchanged:: 1.9 .. 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: Django can serialize the following on Python 3 only:

View File

@ -23,6 +23,7 @@ from django.test import SimpleTestCase, ignore_warnings, mock
from django.utils import datetime_safe, six from django.utils import datetime_safe, six
from django.utils._os import upath from django.utils._os import upath
from django.utils.deconstruct import deconstructible 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.timezone import FixedOffset, get_default_timezone, utc
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
@ -229,6 +230,11 @@ class WriterTests(SimpleTestCase):
("[list, tuple, dict, set, frozenset]", set()) ("[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): def test_serialize_functions(self):
with six.assertRaisesRegex(self, ValueError, 'Cannot serialize function: lambda'): with six.assertRaisesRegex(self, ValueError, 'Cannot serialize function: lambda'):
self.assertSerializedEqual(lambda x: 42) self.assertSerializedEqual(lambda x: 42)