From 0081d744b9f530b2418d1e82d7ad94a2ebc31c9c Mon Sep 17 00:00:00 2001 From: Matteo Suppo Date: Sat, 23 Mar 2013 14:18:11 +0100 Subject: [PATCH 1/5] Added tests for issue 747 in serializer.py --- rest_framework/tests/serializer.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/rest_framework/tests/serializer.py b/rest_framework/tests/serializer.py index 05217f35a..0386ca76e 100644 --- a/rest_framework/tests/serializer.py +++ b/rest_framework/tests/serializer.py @@ -1082,3 +1082,32 @@ class DeserializeListTestCase(TestCase): self.assertFalse(serializer.is_valid()) expected = [{}, {'email': ['This field is required.']}, {}] self.assertEqual(serializer.errors, expected) + + +# test for issue 747 + +class LazyStringModel(object): + def __init__(self, lazystring): + self.lazystring = lazystring + + +class LazyStringSerializer(serializers.Serializer): + lazystring = serializers.Field() + + def restore_object(self, attrs, instance=None): + if instance is not None: + instance.lazystring = attrs.get('lazystring', instance.lazystring) + return instance + return Comment(**attrs) + + +class LazyStringsTestCase(TestCase): + + def setUp(self): + from django.utils.translation import ugettext_lazy as _ + + self.model = LazyStringModel(lazystring=_("lazystring")) + + def test_lazy_strings_are_translated(self): + serializer = LazyStringSerializer(self.model) + self.assertEqual(type(serializer.data['lazystring']), type("lazystring")) From b5640bb77843c50f42a649982b9b9592113c6f59 Mon Sep 17 00:00:00 2001 From: Matteo Suppo Date: Sat, 23 Mar 2013 14:18:55 +0100 Subject: [PATCH 2/5] Forcing translations of lazy translatable strings in Field to_native method --- rest_framework/fields.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rest_framework/fields.py b/rest_framework/fields.py index f3496b53e..09f076ab7 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -18,7 +18,7 @@ from rest_framework import ISO_8601 from rest_framework.compat import timezone, parse_date, parse_datetime, parse_time from rest_framework.compat import BytesIO from rest_framework.compat import six -from rest_framework.compat import smart_text +from rest_framework.compat import smart_text, force_text from rest_framework.settings import api_settings @@ -165,7 +165,7 @@ class Field(object): return [self.to_native(item) for item in value] elif isinstance(value, dict): return dict(map(self.to_native, (k, v)) for k, v in value.items()) - return smart_text(value) + return force_text(value) def attributes(self): """ From ef383d969c440a20fdf25748de590e07cb1abfda Mon Sep 17 00:00:00 2001 From: Ryan Kaskel Date: Sat, 18 May 2013 14:31:29 +0100 Subject: [PATCH 3/5] Clean up test case. --- rest_framework/tests/serializer.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/rest_framework/tests/serializer.py b/rest_framework/tests/serializer.py index d978dc872..e999b624b 100644 --- a/rest_framework/tests/serializer.py +++ b/rest_framework/tests/serializer.py @@ -1,8 +1,9 @@ from __future__ import unicode_literals from django.db import models from django.db.models.fields import BLANK_CHOICE_DASH -from django.utils.datastructures import MultiValueDict from django.test import TestCase +from django.utils.datastructures import MultiValueDict +from django.utils.translation import ugettext_lazy as _ from rest_framework import serializers from rest_framework.tests.models import (HasPositiveIntegerAsChoice, Album, ActionItem, Anchor, BasicModel, BlankFieldModel, BlogPost, BlogPostComment, Book, CallableDefaultValueModel, DefaultValueModel, @@ -1246,6 +1247,7 @@ class DeserializeListTestCase(TestCase): # test for issue 747 + class LazyStringModel(object): def __init__(self, lazystring): self.lazystring = lazystring @@ -1258,16 +1260,14 @@ class LazyStringSerializer(serializers.Serializer): if instance is not None: instance.lazystring = attrs.get('lazystring', instance.lazystring) return instance - return Comment(**attrs) + return LazyStringModel(**attrs) class LazyStringsTestCase(TestCase): - def setUp(self): - from django.utils.translation import ugettext_lazy as _ - - self.model = LazyStringModel(lazystring=_("lazystring")) + self.model = LazyStringModel(lazystring=_('lazystring')) def test_lazy_strings_are_translated(self): serializer = LazyStringSerializer(self.model) - self.assertEqual(type(serializer.data['lazystring']), type("lazystring")) + self.assertEqual(type(serializer.data['lazystring']), + type('lazystring')) From 10e451a85a034d1158148f54e98147e81454c2ed Mon Sep 17 00:00:00 2001 From: Ryan Kaskel Date: Sat, 18 May 2013 16:21:18 +0100 Subject: [PATCH 4/5] Handle Python 3 strings and lazy strings. --- rest_framework/fields.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/rest_framework/fields.py b/rest_framework/fields.py index 2ab603cfb..c3ac62813 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -19,6 +19,7 @@ from django.db.models.fields import BLANK_CHOICE_DASH from django import forms from django.forms import widgets from django.utils.encoding import is_protected_type +from django.utils.functional import Promise from django.utils.translation import ugettext_lazy as _ from django.utils.datastructures import SortedDict @@ -45,6 +46,15 @@ def is_simple_callable(obj): len_defaults = len(defaults) if defaults else 0 return len_args <= len_defaults +if six.PY3: + def is_non_str_iterable(obj): + if (isinstance(obj, str) or + (isinstance(obj, Promise) and obj._delegate_text)): + return False + return hasattr(obj, '__iter__') +else: + def is_non_str_iterable(obj): + return hasattr(obj, '__iter__') def get_component(obj, attr_name): """ @@ -169,7 +179,8 @@ class Field(object): if is_protected_type(value): return value - elif hasattr(value, '__iter__') and not isinstance(value, (dict, six.string_types)): + elif (is_non_str_iterable(value) and + not isinstance(value, (dict, six.string_types))): return [self.to_native(item) for item in value] elif isinstance(value, dict): # Make sure we preserve field ordering, if it exists From 579f77ceaa03a216a7a635c3d3a4d83b0e5868f8 Mon Sep 17 00:00:00 2001 From: Ryan Kaskel Date: Sat, 18 May 2013 17:04:17 +0100 Subject: [PATCH 5/5] Move function to compat. --- rest_framework/compat.py | 13 +++++++++++++ rest_framework/fields.py | 13 +------------ 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/rest_framework/compat.py b/rest_framework/compat.py index cd39f5445..76dc00526 100644 --- a/rest_framework/compat.py +++ b/rest_framework/compat.py @@ -495,3 +495,16 @@ except ImportError: oauth2_provider_forms = None oauth2_provider_scope = None oauth2_constants = None + +# Handle lazy strings +from django.utils.functional import Promise + +if six.PY3: + def is_non_str_iterable(obj): + if (isinstance(obj, str) or + (isinstance(obj, Promise) and obj._delegate_text)): + return False + return hasattr(obj, '__iter__') +else: + def is_non_str_iterable(obj): + return hasattr(obj, '__iter__') diff --git a/rest_framework/fields.py b/rest_framework/fields.py index d5cf30e43..b5f99823a 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -19,7 +19,6 @@ from django.db.models.fields import BLANK_CHOICE_DASH from django import forms from django.forms import widgets from django.utils.encoding import is_protected_type -from django.utils.functional import Promise from django.utils.translation import ugettext_lazy as _ from django.utils.datastructures import SortedDict @@ -27,7 +26,7 @@ from rest_framework import ISO_8601 from rest_framework.compat import timezone, parse_date, parse_datetime, parse_time from rest_framework.compat import BytesIO from rest_framework.compat import six -from rest_framework.compat import smart_text, force_text +from rest_framework.compat import smart_text, force_text, is_non_str_iterable from rest_framework.settings import api_settings @@ -46,16 +45,6 @@ def is_simple_callable(obj): len_defaults = len(defaults) if defaults else 0 return len_args <= len_defaults -if six.PY3: - def is_non_str_iterable(obj): - if (isinstance(obj, str) or - (isinstance(obj, Promise) and obj._delegate_text)): - return False - return hasattr(obj, '__iter__') -else: - def is_non_str_iterable(obj): - return hasattr(obj, '__iter__') - def get_component(obj, attr_name): """ Given an object, and an attribute name,