From 921e4ed2ee11edffd19d2ca40f10d47d2c148ea1 Mon Sep 17 00:00:00 2001 From: Paul Oswald Date: Mon, 28 Jul 2014 16:59:55 +0900 Subject: Evaluate content before passing to regex.sub Issue #1708 --- rest_framework/tests/test_description.py | 22 ++++++++++++++++++++++ rest_framework/utils/formatting.py | 4 +--- 2 files changed, 23 insertions(+), 3 deletions(-) (limited to 'rest_framework') diff --git a/rest_framework/tests/test_description.py b/rest_framework/tests/test_description.py index 4c03c1de..52fa55fb 100644 --- a/rest_framework/tests/test_description.py +++ b/rest_framework/tests/test_description.py @@ -2,6 +2,7 @@ from __future__ import unicode_literals from django.test import TestCase +from django.utils.encoding import python_2_unicode_compatible from rest_framework.compat import apply_markdown, smart_text from rest_framework.views import APIView from rest_framework.tests.description import ViewWithNonASCIICharactersInDocstring @@ -98,6 +99,27 @@ class TestViewNamesAndDescriptions(TestCase): pass self.assertEqual(MockView().get_view_description(), '') + def test_view_description_can_be_promise(self): + """ + Ensure a view may have a docstring that is actually a lazily evaluated + class that can be converted to a string. + + See: https://github.com/tomchristie/django-rest-framework/issues/1708 + """ + # use a mock object instead of gettext_lazy to ensure that we can't end + # up with a test case string in our l10n catalog + @python_2_unicode_compatible + class MockLazyStr(object): + def __init__(self, string): + self.s = string + def __str__(self): + return self.s + + class MockView(APIView): + __doc__ = MockLazyStr("a gettext string") + + self.assertEqual(MockView().get_view_description(), 'a gettext string') + def test_markdown(self): """ Ensure markdown to HTML works as expected. diff --git a/rest_framework/utils/formatting.py b/rest_framework/utils/formatting.py index 4b59ba84..12b79b6c 100644 --- a/rest_framework/utils/formatting.py +++ b/rest_framework/utils/formatting.py @@ -6,8 +6,6 @@ from __future__ import unicode_literals from django.utils.html import escape from django.utils.safestring import mark_safe from rest_framework.compat import apply_markdown -from rest_framework.settings import api_settings -from textwrap import dedent import re @@ -36,7 +34,7 @@ def dedent(content): # unindent the content if needed if whitespace_counts: whitespace_pattern = '^' + (' ' * min(whitespace_counts)) - content = re.sub(re.compile(whitespace_pattern, re.MULTILINE), '', content) + content = re.sub(re.compile(whitespace_pattern, re.MULTILINE), '', unicode(content)) return content.strip() -- cgit v1.2.3 From 66fa40c300b4d3e768b4a7993f020056c44fdda3 Mon Sep 17 00:00:00 2001 From: Paul Oswald Date: Tue, 29 Jul 2014 22:13:11 +0900 Subject: evaluate content at function start --- rest_framework/utils/formatting.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'rest_framework') diff --git a/rest_framework/utils/formatting.py b/rest_framework/utils/formatting.py index 12b79b6c..2b3cbc95 100644 --- a/rest_framework/utils/formatting.py +++ b/rest_framework/utils/formatting.py @@ -28,13 +28,14 @@ def dedent(content): as it fails to dedent multiline docstrings that include unindented text on the initial line. """ + content = unicode(content) whitespace_counts = [len(line) - len(line.lstrip(' ')) for line in content.splitlines()[1:] if line.lstrip()] # unindent the content if needed if whitespace_counts: whitespace_pattern = '^' + (' ' * min(whitespace_counts)) - content = re.sub(re.compile(whitespace_pattern, re.MULTILINE), '', unicode(content)) + content = re.sub(re.compile(whitespace_pattern, re.MULTILINE), '', content) return content.strip() -- cgit v1.2.3 From 192201d5840f13c8b96f44fdce4645edeb653f0f Mon Sep 17 00:00:00 2001 From: Paul Oswald Date: Thu, 7 Aug 2014 15:48:29 +0900 Subject: remove dep on python_2_unicode_compatible python_2_unicode_compatible is not available in all Django versions --- rest_framework/tests/test_description.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'rest_framework') diff --git a/rest_framework/tests/test_description.py b/rest_framework/tests/test_description.py index 52fa55fb..8aa16261 100644 --- a/rest_framework/tests/test_description.py +++ b/rest_framework/tests/test_description.py @@ -2,7 +2,6 @@ from __future__ import unicode_literals from django.test import TestCase -from django.utils.encoding import python_2_unicode_compatible from rest_framework.compat import apply_markdown, smart_text from rest_framework.views import APIView from rest_framework.tests.description import ViewWithNonASCIICharactersInDocstring @@ -108,17 +107,18 @@ class TestViewNamesAndDescriptions(TestCase): """ # use a mock object instead of gettext_lazy to ensure that we can't end # up with a test case string in our l10n catalog - @python_2_unicode_compatible class MockLazyStr(object): def __init__(self, string): self.s = string def __str__(self): return self.s + def __unicode__(self): + return self.s class MockView(APIView): - __doc__ = MockLazyStr("a gettext string") + __doc__ = MockLazyStr(u"a gettext string") - self.assertEqual(MockView().get_view_description(), 'a gettext string') + self.assertEqual(MockView().get_view_description(), u'a gettext string') def test_markdown(self): """ -- cgit v1.2.3 From 3e93c96ece8af010185e1fe1188dd2df569d4528 Mon Sep 17 00:00:00 2001 From: Paul Oswald Date: Tue, 19 Aug 2014 10:09:48 +0900 Subject: replace unicode call with force_text --- rest_framework/utils/formatting.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'rest_framework') diff --git a/rest_framework/utils/formatting.py b/rest_framework/utils/formatting.py index 2b3cbc95..40bced5f 100644 --- a/rest_framework/utils/formatting.py +++ b/rest_framework/utils/formatting.py @@ -5,6 +5,8 @@ from __future__ import unicode_literals from django.utils.html import escape from django.utils.safestring import mark_safe +from django.utils.encoding import force_text + from rest_framework.compat import apply_markdown import re @@ -28,7 +30,7 @@ def dedent(content): as it fails to dedent multiline docstrings that include unindented text on the initial line. """ - content = unicode(content) + content = force_text(content) whitespace_counts = [len(line) - len(line.lstrip(' ')) for line in content.splitlines()[1:] if line.lstrip()] -- cgit v1.2.3 From b554c67d14fb0464106a247e5da96af80b819be9 Mon Sep 17 00:00:00 2001 From: Daniel Roseman Date: Sat, 30 Aug 2014 13:28:12 +0100 Subject: Restore body block to base template. --- rest_framework/templates/rest_framework/base.html | 2 ++ 1 file changed, 2 insertions(+) (limited to 'rest_framework') diff --git a/rest_framework/templates/rest_framework/base.html b/rest_framework/templates/rest_framework/base.html index cee9724d..e54e3814 100644 --- a/rest_framework/templates/rest_framework/base.html +++ b/rest_framework/templates/rest_framework/base.html @@ -25,6 +25,7 @@ {% endblock %} + {% block body %}
@@ -261,4 +262,5 @@ {% endblock %} + {% endblock %} -- cgit v1.2.3 From 1c9c5d5c32656231acf5f14b5231f9274a2eb254 Mon Sep 17 00:00:00 2001 From: Carlton Gibson Date: Mon, 1 Sep 2014 10:07:05 +0200 Subject: Regression for #1810: Test login view renders --- rest_framework/urls.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'rest_framework') diff --git a/rest_framework/urls.py b/rest_framework/urls.py index 8fa3073e..cfcee534 100644 --- a/rest_framework/urls.py +++ b/rest_framework/urls.py @@ -6,7 +6,7 @@ your API requires authentication: urlpatterns = patterns('', ... - url(r'^auth', include('rest_framework.urls', namespace='rest_framework')) + url(r'^auth/', include('rest_framework.urls', namespace='rest_framework')) ) The urls must be namespaced as 'rest_framework', and you should make sure -- cgit v1.2.3 From 55e779c856347094e3240bc7bf83927acf0bd442 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Mon, 1 Sep 2014 09:07:55 +0100 Subject: Version 2.4.1 --- rest_framework/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'rest_framework') diff --git a/rest_framework/__init__.py b/rest_framework/__init__.py index f95bdc22..7c187639 100644 --- a/rest_framework/__init__.py +++ b/rest_framework/__init__.py @@ -8,7 +8,7 @@ ______ _____ _____ _____ __ """ __title__ = 'Django REST framework' -__version__ = '2.4.0' +__version__ = '2.4.1' __author__ = 'Tom Christie' __license__ = 'BSD 2-Clause' __copyright__ = 'Copyright 2011-2014 Tom Christie' -- cgit v1.2.3 From 82d4b2083292659358d5df4d03d2115576e8ae4e Mon Sep 17 00:00:00 2001 From: Timo Tuominen Date: Mon, 1 Sep 2014 12:17:36 +0300 Subject: Add subclass matching to serializer field mapping. --- rest_framework/serializers.py | 3 +++ 1 file changed, 3 insertions(+) (limited to 'rest_framework') diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index be8ad3f2..6d25161e 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -907,6 +907,9 @@ class ModelSerializer(Serializer): try: return self.field_mapping[model_field.__class__](**kwargs) except KeyError: + for model_field_class, serializer_field_class in self.field_mapping.items(): + if isinstance(model_field, model_field_class): + return serializer_field_class(**kwargs) return ModelField(model_field=model_field, **kwargs) def get_validation_exclusions(self, instance=None): -- cgit v1.2.3 From ae84b8b0e8a99261ea2436f77ab5238f21603c0c Mon Sep 17 00:00:00 2001 From: Timo Tuominen Date: Mon, 1 Sep 2014 15:03:39 +0300 Subject: Traverse the method resolution order when mapping serializer fields. --- rest_framework/serializers.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) (limited to 'rest_framework') diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index 6d25161e..f37fbf98 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -904,13 +904,11 @@ class ModelSerializer(Serializer): for attribute in attributes: kwargs.update({attribute: getattr(model_field, attribute)}) - try: - return self.field_mapping[model_field.__class__](**kwargs) - except KeyError: - for model_field_class, serializer_field_class in self.field_mapping.items(): - if isinstance(model_field, model_field_class): - return serializer_field_class(**kwargs) - return ModelField(model_field=model_field, **kwargs) + for model_field_baseclass in inspect.getmro(model_field.__class__): + serializer_field_class = self.field_mapping.get(model_field_baseclass) + if serializer_field_class: + return serializer_field_class(**kwargs) + return ModelField(model_field=model_field, **kwargs) def get_validation_exclusions(self, instance=None): """ -- cgit v1.2.3 From 582f6fdd4b0fb12a7c0d1fefe265499a284c9b79 Mon Sep 17 00:00:00 2001 From: Timo Tuominen Date: Mon, 1 Sep 2014 15:54:33 +0300 Subject: Add utility function to match classes in dictionary. --- rest_framework/serializers.py | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) (limited to 'rest_framework') diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index f37fbf98..5c33300c 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -625,6 +625,21 @@ class ModelSerializerOptions(SerializerOptions): self.write_only_fields = getattr(meta, 'write_only_fields', ()) +def _get_class_mapping(mapping, obj): + """ + Takes a dictionary with classes as keys, and an object. + Traverses the object's inheritance hierarchy in method + resolution order, and returns the first matching value + from the dictionary or None. + + """ + for baseclass in inspect.getmro(obj.__class__): + val = mapping.get(baseclass) + if val: + return val + return None + + class ModelSerializer(Serializer): """ A serializer that deals with model instances and querysets. @@ -899,15 +914,16 @@ class ModelSerializer(Serializer): models.URLField: ['max_length'], } - if model_field.__class__ in attribute_dict: - attributes = attribute_dict[model_field.__class__] + attributes = _get_class_mapping(attribute_dict, model_field) + if attributes: for attribute in attributes: kwargs.update({attribute: getattr(model_field, attribute)}) - for model_field_baseclass in inspect.getmro(model_field.__class__): - serializer_field_class = self.field_mapping.get(model_field_baseclass) - if serializer_field_class: - return serializer_field_class(**kwargs) + serializer_field_class = _get_class_mapping( + self.field_mapping, model_field) + + if serializer_field_class: + return serializer_field_class(**kwargs) return ModelField(model_field=model_field, **kwargs) def get_validation_exclusions(self, instance=None): -- cgit v1.2.3 From e437520217e20d500d641b95482d49484b1f24a7 Mon Sep 17 00:00:00 2001 From: Timo Tuominen Date: Mon, 1 Sep 2014 17:02:48 +0300 Subject: Generator implementation of class mapping. --- rest_framework/serializers.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'rest_framework') diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index 5c33300c..b3db3582 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -633,11 +633,10 @@ def _get_class_mapping(mapping, obj): from the dictionary or None. """ - for baseclass in inspect.getmro(obj.__class__): - val = mapping.get(baseclass) - if val: - return val - return None + return next( + (mapping[cls] for cls in inspect.getmro(obj.__class__) if cls in mapping), + None + ) class ModelSerializer(Serializer): -- cgit v1.2.3 From fa0ef1773773c58b5708abad0e90a44fc9a308f8 Mon Sep 17 00:00:00 2001 From: Carlton Gibson Date: Tue, 2 Sep 2014 14:53:37 +0200 Subject: Remove Login Dropdown when Auth Views are not registered. Fixes #1738 --- rest_framework/templates/rest_framework/base.html | 26 ++++++++--------------- rest_framework/templatetags/rest_framework.py | 19 ++++++++++++----- 2 files changed, 23 insertions(+), 22 deletions(-) (limited to 'rest_framework') diff --git a/rest_framework/templates/rest_framework/base.html b/rest_framework/templates/rest_framework/base.html index e54e3814..5a12277b 100644 --- a/rest_framework/templates/rest_framework/base.html +++ b/rest_framework/templates/rest_framework/base.html @@ -5,14 +5,14 @@ {% block head %} - + {% block meta %} {% endblock %} - + {% block title %}Django REST framework{% endblock %} - + {% block style %} {% block bootstrap_theme %} @@ -21,7 +21,7 @@ {% endblock %} - + {% endblock %} @@ -44,15 +44,7 @@
{% endif %} - + {% if put_form or raw_data_put_form or raw_data_patch_form %}
{% if put_form %} @@ -246,7 +238,7 @@ {% endif %}
- +