From 83f446fc475c8800298bbe5314659e82400a4709 Mon Sep 17 00:00:00 2001 From: Karol Sikora Date: Wed, 26 Nov 2014 16:06:21 +0100 Subject: [PATCH 01/32] Fixed get_component method in Field to get working with subclassess of collections.Mapping --- rest_framework/fields.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rest_framework/fields.py b/rest_framework/fields.py index c0253f86b8..a2d2d5feba 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -10,6 +10,7 @@ import inspect import re import warnings +import collections from decimal import Decimal, DecimalException from django import forms from django.core import validators @@ -52,7 +53,7 @@ def get_component(obj, attr_name): Given an object, and an attribute name, return that attribute on the object. """ - if isinstance(obj, dict): + if isinstance(obj, collections.Mapping): val = obj.get(attr_name) else: val = getattr(obj, attr_name) From 9401eccbfab0ecd5f29414eab01e887c56e6e548 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Mon, 23 Mar 2015 11:20:09 +0000 Subject: [PATCH 02/32] Escape tab switching cookie --- rest_framework/static/rest_framework/js/default.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/rest_framework/static/rest_framework/js/default.js b/rest_framework/static/rest_framework/js/default.js index bcb1964dbe..f04e55696d 100644 --- a/rest_framework/static/rest_framework/js/default.js +++ b/rest_framework/static/rest_framework/js/default.js @@ -43,6 +43,10 @@ $('a[data-toggle="tab"]').click(function(){ var selectedTab = null; var selectedTabName = getCookie('tabstyle'); +if (selectedTabName) { + selectedTabName = selectedTabName.replace(/[^a-z-]/g, ''); +} + if (selectedTabName) { selectedTab = $('.form-switcher a[name=' + selectedTabName + ']'); } From f7fc754bc916ba41288328cc3d0434225739a4f0 Mon Sep 17 00:00:00 2001 From: Andy Freeland Date: Mon, 23 Mar 2015 12:01:54 -0400 Subject: [PATCH 03/32] Version bump and release notes update for 2.4.5 --- docs/topics/release-notes.md | 6 ++++++ rest_framework/__init__.py | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/topics/release-notes.md b/docs/topics/release-notes.md index 11d12ae326..9879c4665f 100644 --- a/docs/topics/release-notes.md +++ b/docs/topics/release-notes.md @@ -40,6 +40,12 @@ You can determine your currently installed version using `pip freeze`: ## 2.4.x series +### 2.4.5 + +**Date**: 24 March 2015 + +* **Security fix**: Escape tab switching cookie name in browsable API. [Backported from 3.1.1](http://www.django-rest-framework.org/topics/release-notes/#311). + ### 2.4.4 **Date**: [3rd November 2014](https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%222.4.4+Release%22+). diff --git a/rest_framework/__init__.py b/rest_framework/__init__.py index 15b12d9bea..5301909716 100644 --- a/rest_framework/__init__.py +++ b/rest_framework/__init__.py @@ -8,7 +8,7 @@ """ __title__ = 'Django REST framework' -__version__ = '2.4.4' +__version__ = '2.4.5' __author__ = 'Tom Christie' __license__ = 'BSD 2-Clause' __copyright__ = 'Copyright 2011-2014 Tom Christie' From 2a6f25c5f9d45cfe04e93e0421ff8982e29986f6 Mon Sep 17 00:00:00 2001 From: Andy Freeland Date: Tue, 24 Mar 2015 15:32:31 -0400 Subject: [PATCH 04/32] Pin flake8 2.4.0 and pep8 1.5.7 --- requirements-test.txt | 3 ++- tox.ini | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/requirements-test.txt b/requirements-test.txt index 411daeba2d..2880f5a987 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -2,7 +2,8 @@ pytest-django==2.6 pytest==2.5.2 pytest-cov==1.6 -flake8==2.2.2 +pep8==1.5.7 +flake8==2.4.0 # Optional packages markdown>=2.1.0 diff --git a/tox.ini b/tox.ini index b3f53cce28..0e17ca5116 100644 --- a/tox.ini +++ b/tox.ini @@ -15,7 +15,8 @@ setenv = [testenv:flake8] basepython = python2.7 deps = pytest==2.5.2 - flake8==2.2.2 + pep8==1.5.7 + flake8==2.4.0 commands = ./runtests.py --lintonly [testenv:py3.4-django1.7] From d1b883e1598dcf25a3104d5f4d91406074d46958 Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Fri, 10 Apr 2015 09:59:18 +0200 Subject: [PATCH 05/32] Update 2.4 docs url Point to version 2 docs instead of the latest and greatest docs. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 428fb8e9d1..469150e93c 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ **Awesome web-browseable Web APIs.** -**Note**: Full documentation for the project is available at [http://www.django-rest-framework.org][docs]. +**Note**: Full documentation for the project is available at [http://tomchristie.github.io/rest-framework-2-docs/][docs]. # Overview @@ -135,7 +135,7 @@ Or to create a new user: # Documentation & Support -Full documentation for the project is available at [http://www.django-rest-framework.org][docs]. +Full documentation for the project is available at [http://tomchristie.github.io/rest-framework-2-docs/][docs]. For questions and support, use the [REST framework discussion group][group], or `#restframework` on freenode IRC. From ee109c446a309382b94b62b626c385f292e28ac9 Mon Sep 17 00:00:00 2001 From: w- Date: Tue, 21 Apr 2015 18:19:33 -0700 Subject: [PATCH 06/32] increase serializer compatibility to django 1.8 i ran into this issue when using v2.4 with django v1.8. (i didn't previously read it isn't supported) It's not in the release notes but django.db.model.Options many_to_many() now returns an ImmutableList which is really just a tuple with a bunch of warnings and hooks on it. if we don't make this typecast change we get the following error TypeError: can only concatenate tuple (not "list") to tuple I'm not sure if this change is appropriate and not sure what, if any, additional tests to include with this . I attempted to test this but must be doing something wrong. Every test fails when trying to use cursor. most of the errors I get are Failed: Database access not allowed, use the "django_db" mark to enable i followed the instructions here. https://github.com/tomchristie/django-rest-framework/blob/version-2.4.x/CONTRIBUTING.md --- rest_framework/serializers.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index 7d85894f63..781c97dd81 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -986,7 +986,11 @@ def restore_object(self, attrs, instance=None): m2m_data[field_name] = attrs.pop(field_name) # Forward m2m relations - for field in meta.many_to_many + meta.virtual_fields: + if issubclass(meta.many_to_many.__class__, tuple): + temp_m2m = list(meta.many_to_many) + else: + temp_m2m = meta.many_to_many + for field in temp_m2m + meta.virtual_fields: if isinstance(field, GenericForeignKey): continue if field.name in attrs: From 5eacaecbec1b94baa332b40ccb60e275d85fcfd6 Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Wed, 22 Apr 2015 09:37:23 +0200 Subject: [PATCH 07/32] Update 2.4 docs url for real Missed the important hunk --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 469150e93c..fd91064168 100644 --- a/README.md +++ b/README.md @@ -202,7 +202,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. [wlonk]: https://twitter.com/wlonk/status/261689665952833536 [laserllama]: https://twitter.com/laserllama/status/328688333750407168 -[docs]: http://www.django-rest-framework.org/ +[docs]: http://tomchristie.github.io/rest-framework-2-docs/ [urlobject]: https://github.com/zacharyvoase/urlobject [markdown]: http://pypi.python.org/pypi/Markdown/ [pyyaml]: http://pypi.python.org/pypi/PyYAML From 4a75a9c0cb0e2a4873e59ae596b1c41753299d28 Mon Sep 17 00:00:00 2001 From: Xavier Ordoquy Date: Wed, 22 Apr 2015 07:34:17 +0200 Subject: [PATCH 08/32] Version 2.4.5 Update release script. --- setup.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 2c56cd758f..f9c10456bc 100755 --- a/setup.py +++ b/setup.py @@ -59,8 +59,14 @@ def get_package_data(package): if sys.argv[-1] == 'publish': - os.system("python setup.py sdist upload") - os.system("python setup.py bdist_wheel upload") + if os.system("pip freeze | grep wheel"): + print("wheel not installed.\nUse `pip install wheel`.\nExiting.") + sys.exit() + if os.system("pip freeze | grep twine"): + print("twine not installed.\nUse `pip install twine`.\nExiting.") + sys.exit() + os.system("python setup.py sdist bdist_wheel") + os.system("twine upload dist/*") print("You probably want to also tag the version now:") print(" git tag -a %s -m 'version %s'" % (version, version)) print(" git push --tags") From 73978c95607f40f333ccfa3a9c202bde18e1d395 Mon Sep 17 00:00:00 2001 From: kazmiruk Date: Wed, 22 Apr 2015 13:12:12 +0700 Subject: [PATCH 09/32] change SortedDict to OrderedDict --- rest_framework/fields.py | 5 ++--- rest_framework/routers.py | 7 +++---- rest_framework/serializers.py | 27 +++++++++++++++------------ rest_framework/utils/encoders.py | 12 ++++++------ rest_framework/views.py | 4 ++-- 5 files changed, 28 insertions(+), 27 deletions(-) diff --git a/rest_framework/fields.py b/rest_framework/fields.py index a2d2d5feba..aad49ed5ca 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -22,7 +22,6 @@ from django.utils import six, timezone from django.utils.encoding import is_protected_type from django.utils.translation import ugettext_lazy as _ -from django.utils.datastructures import SortedDict from django.utils.dateparse import parse_date, parse_datetime, parse_time from rest_framework import ISO_8601 from rest_framework.compat import ( @@ -225,7 +224,7 @@ def to_native(self, value): return [self.to_native(item) for item in value] elif isinstance(value, dict): # Make sure we preserve field ordering, if it exists - ret = SortedDict() + ret = collections.OrderedDict() for key, val in value.items(): ret[key] = self.to_native(val) return ret @@ -240,7 +239,7 @@ def attributes(self): return {} def metadata(self): - metadata = SortedDict() + metadata = collections.OrderedDict() metadata['type'] = self.type_label metadata['required'] = getattr(self, 'required', False) optional_attrs = ['read_only', 'label', 'help_text', diff --git a/rest_framework/routers.py b/rest_framework/routers.py index 169e6e8bc4..9937566d2b 100644 --- a/rest_framework/routers.py +++ b/rest_framework/routers.py @@ -16,11 +16,10 @@ from __future__ import unicode_literals import itertools -from collections import namedtuple +from collections import namedtuple, OrderedDict from django.conf.urls import patterns, url from django.core.exceptions import ImproperlyConfigured from django.core.urlresolvers import NoReverseMatch -from django.utils.datastructures import SortedDict from rest_framework import views from rest_framework.response import Response from rest_framework.reverse import reverse @@ -278,7 +277,7 @@ def get_api_root_view(self): """ Return a view to use as the API root. """ - api_root_dict = SortedDict() + api_root_dict = OrderedDict() list_name = self.routes[0].name for prefix, viewset, basename in self.registry: api_root_dict[prefix] = list_name.format(basename=basename) @@ -287,7 +286,7 @@ class APIRoot(views.APIView): _ignore_model_permissions = True def get(self, request, *args, **kwargs): - ret = SortedDict() + ret = OrderedDict() for key, url_name in api_root_dict.items(): try: ret[key] = reverse( diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index 7d85894f63..29187c92cd 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -11,6 +11,7 @@ response content is handled by parsers and renderers. """ from __future__ import unicode_literals +from collections import OrderedDict import copy import datetime import inspect @@ -21,7 +22,6 @@ from django.db import models from django.forms import widgets from django.utils import six -from django.utils.datastructures import SortedDict from django.utils.functional import cached_property from django.core.exceptions import ObjectDoesNotExist from rest_framework.settings import api_settings @@ -106,7 +106,7 @@ def __getstate__(self): return dict(self) -class SortedDictWithMetadata(SortedDict): +class OrderedDictWithMetadata(OrderedDict): """ A sorted dict-like object, that can have additional properties attached. """ @@ -116,7 +116,7 @@ def __getstate__(self): Overriden to remove the metadata from the dict, since it shouldn't be pickle and may in some instances be unpickleable. """ - return SortedDict(self).__dict__ + return OrderedDict(self).__dict__ def _is_protected_type(obj): @@ -152,7 +152,7 @@ def _get_declared_fields(bases, attrs): if hasattr(base, 'base_fields'): fields = list(base.base_fields.items()) + fields - return SortedDict(fields) + return OrderedDict(fields) class SerializerMetaclass(type): @@ -180,7 +180,7 @@ class Meta(object): pass _options_class = SerializerOptions - _dict_class = SortedDictWithMetadata + _dict_class = OrderedDictWithMetadata def __init__(self, instance=None, data=None, files=None, context=None, partial=False, many=False, @@ -229,7 +229,7 @@ def get_fields(self): This will be the set of any explicitly declared fields, plus the set of fields returned by get_default_fields(). """ - ret = SortedDict() + ret = OrderedDict() # Get the explicitly declared fields base_fields = copy.deepcopy(self.base_fields) @@ -245,7 +245,7 @@ def get_fields(self): # If 'fields' is specified, use those fields, in that order. if self.opts.fields: assert isinstance(self.opts.fields, (list, tuple)), '`fields` must be a list or tuple' - new = SortedDict() + new = OrderedDict() for key in self.opts.fields: new[key] = ret[key] ret = new @@ -606,7 +606,7 @@ def metadata(self): Useful for things like responding to OPTIONS requests, or generating API schemas for auto-documentation. """ - return SortedDict( + return OrderedDict( [ (field_name, field.metadata()) for field_name, field in six.iteritems(self.fields) @@ -683,7 +683,7 @@ def get_default_fields(self): self.__class__.__name__ ) opts = cls._meta.concrete_model._meta - ret = SortedDict() + ret = OrderedDict() nested = bool(self.opts.depth) # Deal with adding the primary key field @@ -985,13 +985,16 @@ def restore_object(self, attrs, instance=None): if field_name in attrs: m2m_data[field_name] = attrs.pop(field_name) - # Forward m2m relations - for field in meta.many_to_many + meta.virtual_fields: + def _inner_loop_code(field): if isinstance(field, GenericForeignKey): - continue + return if field.name in attrs: m2m_data[field.name] = attrs.pop(field.name) + # Forward m2m relations + _ = [_inner_loop_code(field) for field in meta.many_to_many] + _ = [_inner_loop_code(field) for field in meta.virtual_fields] + # Nested forward relations - These need to be marked so we can save # them before saving the parent model instance. for field_name in attrs.keys(): diff --git a/rest_framework/utils/encoders.py b/rest_framework/utils/encoders.py index 00ffdfbae5..1e570cdde8 100644 --- a/rest_framework/utils/encoders.py +++ b/rest_framework/utils/encoders.py @@ -2,12 +2,12 @@ Helper classes for parsers. """ from __future__ import unicode_literals +from collections import OrderedDict from django.utils import timezone from django.db.models.query import QuerySet -from django.utils.datastructures import SortedDict from django.utils.functional import Promise from rest_framework.compat import force_text -from rest_framework.serializers import DictWithMetadata, SortedDictWithMetadata +from rest_framework.serializers import DictWithMetadata, OrderedDictWithMetadata import datetime import decimal import types @@ -67,7 +67,7 @@ def default(self, o): class SafeDumper(yaml.SafeDumper): """ Handles decimals as strings. - Handles SortedDicts as usual dicts, but preserves field order, rather + Handles OrderedDicts as usual dicts, but preserves field order, rather than the usual behaviour of sorting the keys. """ def represent_decimal(self, data): @@ -81,7 +81,7 @@ def represent_mapping(self, tag, mapping, flow_style=None): best_style = True if hasattr(mapping, 'items'): mapping = list(mapping.items()) - if not isinstance(mapping, SortedDict): + if not isinstance(mapping, OrderedDict): mapping.sort() for item_key, item_value in mapping: node_key = self.represent_data(item_key) @@ -103,7 +103,7 @@ def represent_mapping(self, tag, mapping, flow_style=None): SafeDumper.represent_decimal ) SafeDumper.add_representer( - SortedDict, + OrderedDict, yaml.representer.SafeRepresenter.represent_dict ) SafeDumper.add_representer( @@ -111,7 +111,7 @@ def represent_mapping(self, tag, mapping, flow_style=None): yaml.representer.SafeRepresenter.represent_dict ) SafeDumper.add_representer( - SortedDictWithMetadata, + OrderedDictWithMetadata, yaml.representer.SafeRepresenter.represent_dict ) SafeDumper.add_representer( diff --git a/rest_framework/views.py b/rest_framework/views.py index 38346ab799..89b5921773 100644 --- a/rest_framework/views.py +++ b/rest_framework/views.py @@ -3,9 +3,9 @@ """ from __future__ import unicode_literals +from collections import OrderedDict from django.core.exceptions import PermissionDenied from django.http import Http404 -from django.utils.datastructures import SortedDict from django.views.decorators.csrf import csrf_exempt from rest_framework import status, exceptions from rest_framework.compat import smart_text, HttpResponseBase, View @@ -421,7 +421,7 @@ def metadata(self, request): # By default we can't provide any form-like information, however the # generic views override this implementation and add additional # information for POST and PUT methods, based on the serializer. - ret = SortedDict() + ret = OrderedDict() ret['name'] = self.get_view_name() ret['description'] = self.get_view_description() ret['renders'] = [renderer.media_type for renderer in self.renderer_classes] From 51650f88b7d0d0782885f452466d8a3cbbb9e8ed Mon Sep 17 00:00:00 2001 From: kazmiruk Date: Wed, 22 Apr 2015 13:31:49 +0700 Subject: [PATCH 10/32] fix flake warning --- rest_framework/serializers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index 29187c92cd..ec8977a55b 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -992,8 +992,8 @@ def _inner_loop_code(field): m2m_data[field.name] = attrs.pop(field.name) # Forward m2m relations - _ = [_inner_loop_code(field) for field in meta.many_to_many] - _ = [_inner_loop_code(field) for field in meta.virtual_fields] + [_inner_loop_code(field) for field in meta.many_to_many] + [_inner_loop_code(field) for field in meta.virtual_fields] # Nested forward relations - These need to be marked so we can save # them before saving the parent model instance. From e4e3f57321e7f32b889ccb7d1bfc354eb1cbd101 Mon Sep 17 00:00:00 2001 From: kazmiruk Date: Wed, 22 Apr 2015 13:33:58 +0700 Subject: [PATCH 11/32] fix test for OrderedDict --- tests/test_fields.py | 4 ++-- tests/test_serializer.py | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/test_fields.py b/tests/test_fields.py index 0ddbe48b5b..8076762306 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -3,6 +3,7 @@ """ from __future__ import unicode_literals +from collections import OrderedDict import datetime import re from decimal import Decimal @@ -10,7 +11,6 @@ from django.core import validators from django.db import models from django.test import TestCase -from django.utils.datastructures import SortedDict from rest_framework import serializers from tests.models import RESTFrameworkModel @@ -95,7 +95,7 @@ def test_dict_field_ordering(self): Field should preserve dictionary ordering, if it exists. See: https://github.com/tomchristie/django-rest-framework/issues/832 """ - ret = SortedDict() + ret = OrderedDict() ret['c'] = 1 ret['b'] = 1 ret['a'] = 1 diff --git a/tests/test_serializer.py b/tests/test_serializer.py index e72b723f05..c0de5cf274 100644 --- a/tests/test_serializer.py +++ b/tests/test_serializer.py @@ -1297,7 +1297,7 @@ def test_pickle_simple_model_serializer_data(self): def test_pickle_inner_serializer(self): """ - Test pickling a serializer whose resulting .data (a SortedDictWithMetadata) will + Test pickling a serializer whose resulting .data (a OrderedDictWithMetadata) will have unpickleable meta data--in order to make sure metadata doesn't get pulled into the pickle. See DictWithMetadata.__getstate__ """ @@ -1312,13 +1312,13 @@ def test_getstate_method_should_not_return_none(self): Regression test for #645. """ data = serializers.DictWithMetadata({1: 1}) - self.assertEqual(data.__getstate__(), serializers.SortedDict({1: 1})) + self.assertEqual(data.__getstate__(), serializers.OrderedDict({1: 1})) def test_serializer_data_is_pickleable(self): """ Another regression test for #645. """ - data = serializers.SortedDictWithMetadata({1: 1}) + data = serializers.OrderedDictWithMetadata({1: 1}) repr(pickle.loads(pickle.dumps(data, 0))) From 47c61679a58a3b01f36991023b19f4bf83201f3e Mon Sep 17 00:00:00 2001 From: kazmiruk Date: Wed, 22 Apr 2015 13:43:16 +0700 Subject: [PATCH 12/32] adds backward compatibility --- rest_framework/fields.py | 9 +++++++-- rest_framework/routers.py | 7 ++++++- rest_framework/serializers.py | 6 +++++- rest_framework/utils/encoders.py | 6 +++++- rest_framework/views.py | 6 +++++- tests/test_fields.py | 6 +++++- 6 files changed, 33 insertions(+), 7 deletions(-) diff --git a/rest_framework/fields.py b/rest_framework/fields.py index aad49ed5ca..6088cdee72 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -30,6 +30,11 @@ ) from rest_framework.settings import api_settings +try: + from collections import OrderedDict +except ImportError: + from django.utils.datastructures import SortedDict as OrderedDict + def is_simple_callable(obj): """ @@ -224,7 +229,7 @@ def to_native(self, value): return [self.to_native(item) for item in value] elif isinstance(value, dict): # Make sure we preserve field ordering, if it exists - ret = collections.OrderedDict() + ret = OrderedDict() for key, val in value.items(): ret[key] = self.to_native(val) return ret @@ -239,7 +244,7 @@ def attributes(self): return {} def metadata(self): - metadata = collections.OrderedDict() + metadata = OrderedDict() metadata['type'] = self.type_label metadata['required'] = getattr(self, 'required', False) optional_attrs = ['read_only', 'label', 'help_text', diff --git a/rest_framework/routers.py b/rest_framework/routers.py index 9937566d2b..4ef7707bbc 100644 --- a/rest_framework/routers.py +++ b/rest_framework/routers.py @@ -16,7 +16,7 @@ from __future__ import unicode_literals import itertools -from collections import namedtuple, OrderedDict +from collections import namedtuple from django.conf.urls import patterns, url from django.core.exceptions import ImproperlyConfigured from django.core.urlresolvers import NoReverseMatch @@ -25,6 +25,11 @@ from rest_framework.reverse import reverse from rest_framework.urlpatterns import format_suffix_patterns +try: + from collections import OrderedDict +except ImportError: + from django.utils.datastructures import SortedDict as OrderedDict + Route = namedtuple('Route', ['url', 'mapping', 'name', 'initkwargs']) DynamicDetailRoute = namedtuple('DynamicDetailRoute', ['url', 'name', 'initkwargs']) diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index ec8977a55b..d3d08f2ff7 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -11,7 +11,6 @@ response content is handled by parsers and renderers. """ from __future__ import unicode_literals -from collections import OrderedDict import copy import datetime import inspect @@ -37,6 +36,11 @@ from rest_framework.relations import * # NOQA from rest_framework.fields import * # NOQA +try: + from collections import OrderedDict +except ImportError: + from django.utils.datastructures import SortedDict as OrderedDict + def _resolve_model(obj): """ diff --git a/rest_framework/utils/encoders.py b/rest_framework/utils/encoders.py index 1e570cdde8..c6ebdcffca 100644 --- a/rest_framework/utils/encoders.py +++ b/rest_framework/utils/encoders.py @@ -2,7 +2,6 @@ Helper classes for parsers. """ from __future__ import unicode_literals -from collections import OrderedDict from django.utils import timezone from django.db.models.query import QuerySet from django.utils.functional import Promise @@ -13,6 +12,11 @@ import types import json +try: + from collections import OrderedDict +except ImportError: + from django.utils.datastructures import SortedDict as OrderedDict + class JSONEncoder(json.JSONEncoder): """ diff --git a/rest_framework/views.py b/rest_framework/views.py index 89b5921773..3852d8e156 100644 --- a/rest_framework/views.py +++ b/rest_framework/views.py @@ -3,7 +3,6 @@ """ from __future__ import unicode_literals -from collections import OrderedDict from django.core.exceptions import PermissionDenied from django.http import Http404 from django.views.decorators.csrf import csrf_exempt @@ -14,6 +13,11 @@ from rest_framework.settings import api_settings from rest_framework.utils import formatting +try: + from collections import OrderedDict +except ImportError: + from django.utils.datastructures import SortedDict as OrderedDict + def get_view_name(view_cls, suffix=None): """ diff --git a/tests/test_fields.py b/tests/test_fields.py index 8076762306..261ef521cb 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -3,7 +3,6 @@ """ from __future__ import unicode_literals -from collections import OrderedDict import datetime import re from decimal import Decimal @@ -14,6 +13,11 @@ from rest_framework import serializers from tests.models import RESTFrameworkModel +try: + from collections import OrderedDict +except ImportError: + from django.utils.datastructures import SortedDict as OrderedDict + class TimestampedModel(models.Model): added = models.DateTimeField(auto_now_add=True) From b3e03cd8e2dfaefcba13ecffe03714a44bf632e3 Mon Sep 17 00:00:00 2001 From: kazmiruk Date: Wed, 22 Apr 2015 19:52:23 +0700 Subject: [PATCH 13/32] fixes broken test + importlib warnings --- rest_framework/serializers.py | 5 ++++- rest_framework/settings.py | 7 ++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index d3d08f2ff7..ceab6111fa 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -16,7 +16,7 @@ import inspect import types from decimal import Decimal -from django.contrib.contenttypes.generic import GenericForeignKey +from django.contrib.contenttypes.fields import GenericForeignKey from django.core.paginator import Page from django.db import models from django.forms import widgets @@ -114,6 +114,9 @@ class OrderedDictWithMetadata(OrderedDict): """ A sorted dict-like object, that can have additional properties attached. """ + def __reduce__(self): + return self.__class__, (OrderedDict(self), ) + def __getstate__(self): """ Used by pickle (e.g., caching). diff --git a/rest_framework/settings.py b/rest_framework/settings.py index 644751f877..c7830b6658 100644 --- a/rest_framework/settings.py +++ b/rest_framework/settings.py @@ -19,9 +19,14 @@ """ from __future__ import unicode_literals from django.conf import settings -from django.utils import importlib, six +from django.utils import six from rest_framework import ISO_8601 +try: + import importlib +except ImportError: + from django.utils import importlib + USER_SETTINGS = getattr(settings, 'REST_FRAMEWORK', None) From 1116a534d447998b46089a0e0e699e76a97fd87f Mon Sep 17 00:00:00 2001 From: kazmiruk Date: Wed, 22 Apr 2015 20:05:10 +0700 Subject: [PATCH 14/32] add compatibility for django 1.6 --- rest_framework/serializers.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index ceab6111fa..34f9f6fcc2 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -16,7 +16,7 @@ import inspect import types from decimal import Decimal -from django.contrib.contenttypes.fields import GenericForeignKey +import django from django.core.paginator import Page from django.db import models from django.forms import widgets @@ -41,6 +41,11 @@ except ImportError: from django.utils.datastructures import SortedDict as OrderedDict +if django.VERSION >= (1, 8): + from django.contrib.contenttypes.fields import GenericForeignKey +else: + from django.contrib.contenttypes.generic import GenericForeignKey + def _resolve_model(obj): """ From 21bac85489a03b0a481347cc3268640adf9f6ce8 Mon Sep 17 00:00:00 2001 From: kazmiruk Date: Wed, 22 Apr 2015 20:52:33 +0700 Subject: [PATCH 15/32] move compatibility checks into compat.py --- rest_framework/compat.py | 10 ++++++++++ rest_framework/fields.py | 7 +------ rest_framework/routers.py | 6 +----- rest_framework/serializers.py | 11 +---------- rest_framework/utils/encoders.py | 7 +------ rest_framework/views.py | 7 +------ 6 files changed, 15 insertions(+), 33 deletions(-) diff --git a/rest_framework/compat.py b/rest_framework/compat.py index fa0f0bfb17..29036b1de6 100644 --- a/rest_framework/compat.py +++ b/rest_framework/compat.py @@ -265,3 +265,13 @@ def python_2_unicode_compatible(klass): klass.__unicode__ = klass.__str__ klass.__str__ = lambda self: self.__unicode__().encode('utf-8') return klass + +try: + from collections import OrderedDict +except ImportError: + from django.utils.datastructures import SortedDict as OrderedDict + +if django.VERSION >= (1, 8): + from django.contrib.contenttypes.fields import GenericForeignKey +else: + from django.contrib.contenttypes.generic import GenericForeignKey diff --git a/rest_framework/fields.py b/rest_framework/fields.py index 6088cdee72..9e95e6dc2a 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -25,16 +25,11 @@ from django.utils.dateparse import parse_date, parse_datetime, parse_time from rest_framework import ISO_8601 from rest_framework.compat import ( - BytesIO, smart_text, + BytesIO, smart_text, OrderedDict, force_text, is_non_str_iterable ) from rest_framework.settings import api_settings -try: - from collections import OrderedDict -except ImportError: - from django.utils.datastructures import SortedDict as OrderedDict - def is_simple_callable(obj): """ diff --git a/rest_framework/routers.py b/rest_framework/routers.py index 4ef7707bbc..6e99f14dec 100644 --- a/rest_framework/routers.py +++ b/rest_framework/routers.py @@ -21,15 +21,11 @@ from django.core.exceptions import ImproperlyConfigured from django.core.urlresolvers import NoReverseMatch from rest_framework import views +from rest_framework.compat import OrderedDict from rest_framework.response import Response from rest_framework.reverse import reverse from rest_framework.urlpatterns import format_suffix_patterns -try: - from collections import OrderedDict -except ImportError: - from django.utils.datastructures import SortedDict as OrderedDict - Route = namedtuple('Route', ['url', 'mapping', 'name', 'initkwargs']) DynamicDetailRoute = namedtuple('DynamicDetailRoute', ['url', 'name', 'initkwargs']) diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index 34f9f6fcc2..0b05ace488 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -23,6 +23,7 @@ from django.utils import six from django.utils.functional import cached_property from django.core.exceptions import ObjectDoesNotExist +from rest_framework.compat import OrderedDict, GenericForeignKey from rest_framework.settings import api_settings @@ -36,16 +37,6 @@ from rest_framework.relations import * # NOQA from rest_framework.fields import * # NOQA -try: - from collections import OrderedDict -except ImportError: - from django.utils.datastructures import SortedDict as OrderedDict - -if django.VERSION >= (1, 8): - from django.contrib.contenttypes.fields import GenericForeignKey -else: - from django.contrib.contenttypes.generic import GenericForeignKey - def _resolve_model(obj): """ diff --git a/rest_framework/utils/encoders.py b/rest_framework/utils/encoders.py index c6ebdcffca..c2bb60c672 100644 --- a/rest_framework/utils/encoders.py +++ b/rest_framework/utils/encoders.py @@ -5,18 +5,13 @@ from django.utils import timezone from django.db.models.query import QuerySet from django.utils.functional import Promise -from rest_framework.compat import force_text +from rest_framework.compat import force_text, OrderedDict from rest_framework.serializers import DictWithMetadata, OrderedDictWithMetadata import datetime import decimal import types import json -try: - from collections import OrderedDict -except ImportError: - from django.utils.datastructures import SortedDict as OrderedDict - class JSONEncoder(json.JSONEncoder): """ diff --git a/rest_framework/views.py b/rest_framework/views.py index 3852d8e156..526931e607 100644 --- a/rest_framework/views.py +++ b/rest_framework/views.py @@ -7,17 +7,12 @@ from django.http import Http404 from django.views.decorators.csrf import csrf_exempt from rest_framework import status, exceptions -from rest_framework.compat import smart_text, HttpResponseBase, View +from rest_framework.compat import smart_text, HttpResponseBase, OrderedDict, View from rest_framework.request import Request from rest_framework.response import Response from rest_framework.settings import api_settings from rest_framework.utils import formatting -try: - from collections import OrderedDict -except ImportError: - from django.utils.datastructures import SortedDict as OrderedDict - def get_view_name(view_cls, suffix=None): """ From 18f1f5784669a026c3c68b6ab7428854a5f9d061 Mon Sep 17 00:00:00 2001 From: kazmiruk Date: Wed, 22 Apr 2015 20:58:19 +0700 Subject: [PATCH 16/32] fixes flake8 warning --- rest_framework/serializers.py | 1 - 1 file changed, 1 deletion(-) diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index 0b05ace488..14d614e65e 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -16,7 +16,6 @@ import inspect import types from decimal import Decimal -import django from django.core.paginator import Page from django.db import models from django.forms import widgets From be66e15c1c14855970c9b33b9c027e67ea31d9ea Mon Sep 17 00:00:00 2001 From: kazmiruk Date: Thu, 23 Apr 2015 16:29:39 +0700 Subject: [PATCH 17/32] renaming of OrderedDict back to SortedDict + some comments --- rest_framework/compat.py | 13 +++++++++++-- rest_framework/fields.py | 6 +++--- rest_framework/routers.py | 6 +++--- rest_framework/serializers.py | 27 ++++++++++++++++----------- rest_framework/utils/encoders.py | 12 ++++++------ rest_framework/views.py | 4 ++-- tests/test_serializer.py | 4 ++-- 7 files changed, 43 insertions(+), 29 deletions(-) diff --git a/rest_framework/compat.py b/rest_framework/compat.py index 29036b1de6..8532a01725 100644 --- a/rest_framework/compat.py +++ b/rest_framework/compat.py @@ -266,11 +266,20 @@ def python_2_unicode_compatible(klass): klass.__str__ = lambda self: self.__unicode__().encode('utf-8') return klass +""" +SortedDict deprecated since django 1.8. There is collections.OrderedDict +which available since python 2.7 and python 3.1. There are no need of other +checks because of django 1.7+ requires python 2.7+ +""" try: - from collections import OrderedDict + from collections import OrderedDict as SortedDict except ImportError: - from django.utils.datastructures import SortedDict as OrderedDict + from django.utils.datastructures import SortedDict +""" +GenericForeignKey moves from generic to fields in django 1.9 and in 1.8 shows +deprecation warnings +""" if django.VERSION >= (1, 8): from django.contrib.contenttypes.fields import GenericForeignKey else: diff --git a/rest_framework/fields.py b/rest_framework/fields.py index 9e95e6dc2a..d310c5df36 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -25,7 +25,7 @@ from django.utils.dateparse import parse_date, parse_datetime, parse_time from rest_framework import ISO_8601 from rest_framework.compat import ( - BytesIO, smart_text, OrderedDict, + BytesIO, smart_text, SortedDict, force_text, is_non_str_iterable ) from rest_framework.settings import api_settings @@ -224,7 +224,7 @@ def to_native(self, value): return [self.to_native(item) for item in value] elif isinstance(value, dict): # Make sure we preserve field ordering, if it exists - ret = OrderedDict() + ret = SortedDict() for key, val in value.items(): ret[key] = self.to_native(val) return ret @@ -239,7 +239,7 @@ def attributes(self): return {} def metadata(self): - metadata = OrderedDict() + metadata = SortedDict() metadata['type'] = self.type_label metadata['required'] = getattr(self, 'required', False) optional_attrs = ['read_only', 'label', 'help_text', diff --git a/rest_framework/routers.py b/rest_framework/routers.py index 6e99f14dec..9e5813b398 100644 --- a/rest_framework/routers.py +++ b/rest_framework/routers.py @@ -21,7 +21,7 @@ from django.core.exceptions import ImproperlyConfigured from django.core.urlresolvers import NoReverseMatch from rest_framework import views -from rest_framework.compat import OrderedDict +from rest_framework.compat import SortedDict from rest_framework.response import Response from rest_framework.reverse import reverse from rest_framework.urlpatterns import format_suffix_patterns @@ -278,7 +278,7 @@ def get_api_root_view(self): """ Return a view to use as the API root. """ - api_root_dict = OrderedDict() + api_root_dict = SortedDict() list_name = self.routes[0].name for prefix, viewset, basename in self.registry: api_root_dict[prefix] = list_name.format(basename=basename) @@ -287,7 +287,7 @@ class APIRoot(views.APIView): _ignore_model_permissions = True def get(self, request, *args, **kwargs): - ret = OrderedDict() + ret = SortedDict() for key, url_name in api_root_dict.items(): try: ret[key] = reverse( diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index 14d614e65e..79984526db 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -22,7 +22,7 @@ from django.utils import six from django.utils.functional import cached_property from django.core.exceptions import ObjectDoesNotExist -from rest_framework.compat import OrderedDict, GenericForeignKey +from rest_framework.compat import SortedDict, GenericForeignKey from rest_framework.settings import api_settings @@ -105,20 +105,25 @@ def __getstate__(self): return dict(self) -class OrderedDictWithMetadata(OrderedDict): +class SortedDictWithMetadata(SortedDict): """ A sorted dict-like object, that can have additional properties attached. """ def __reduce__(self): - return self.__class__, (OrderedDict(self), ) + """ + Used by pickle (e.g., caching) if OrderedDict is used instead of SortedDict + Overriden to remove the metadata from the dict, since it shouldn't be + pickle and may in some instances be unpickleable. + """ + return self.__class__, (SortedDict(self), ) def __getstate__(self): """ - Used by pickle (e.g., caching). + Used by pickle (e.g., caching) in SortedDict Overriden to remove the metadata from the dict, since it shouldn't be pickle and may in some instances be unpickleable. """ - return OrderedDict(self).__dict__ + return SortedDict(self).__dict__ def _is_protected_type(obj): @@ -154,7 +159,7 @@ def _get_declared_fields(bases, attrs): if hasattr(base, 'base_fields'): fields = list(base.base_fields.items()) + fields - return OrderedDict(fields) + return SortedDict(fields) class SerializerMetaclass(type): @@ -182,7 +187,7 @@ class Meta(object): pass _options_class = SerializerOptions - _dict_class = OrderedDictWithMetadata + _dict_class = SortedDictWithMetadata def __init__(self, instance=None, data=None, files=None, context=None, partial=False, many=False, @@ -231,7 +236,7 @@ def get_fields(self): This will be the set of any explicitly declared fields, plus the set of fields returned by get_default_fields(). """ - ret = OrderedDict() + ret = SortedDict() # Get the explicitly declared fields base_fields = copy.deepcopy(self.base_fields) @@ -247,7 +252,7 @@ def get_fields(self): # If 'fields' is specified, use those fields, in that order. if self.opts.fields: assert isinstance(self.opts.fields, (list, tuple)), '`fields` must be a list or tuple' - new = OrderedDict() + new = SortedDict() for key in self.opts.fields: new[key] = ret[key] ret = new @@ -608,7 +613,7 @@ def metadata(self): Useful for things like responding to OPTIONS requests, or generating API schemas for auto-documentation. """ - return OrderedDict( + return SortedDict( [ (field_name, field.metadata()) for field_name, field in six.iteritems(self.fields) @@ -685,7 +690,7 @@ def get_default_fields(self): self.__class__.__name__ ) opts = cls._meta.concrete_model._meta - ret = OrderedDict() + ret = SortedDict() nested = bool(self.opts.depth) # Deal with adding the primary key field diff --git a/rest_framework/utils/encoders.py b/rest_framework/utils/encoders.py index c2bb60c672..813108b777 100644 --- a/rest_framework/utils/encoders.py +++ b/rest_framework/utils/encoders.py @@ -5,8 +5,8 @@ from django.utils import timezone from django.db.models.query import QuerySet from django.utils.functional import Promise -from rest_framework.compat import force_text, OrderedDict -from rest_framework.serializers import DictWithMetadata, OrderedDictWithMetadata +from rest_framework.compat import force_text, SortedDict +from rest_framework.serializers import DictWithMetadata, SortedDictWithMetadata import datetime import decimal import types @@ -66,7 +66,7 @@ def default(self, o): class SafeDumper(yaml.SafeDumper): """ Handles decimals as strings. - Handles OrderedDicts as usual dicts, but preserves field order, rather + Handles SortedDicts as usual dicts, but preserves field order, rather than the usual behaviour of sorting the keys. """ def represent_decimal(self, data): @@ -80,7 +80,7 @@ def represent_mapping(self, tag, mapping, flow_style=None): best_style = True if hasattr(mapping, 'items'): mapping = list(mapping.items()) - if not isinstance(mapping, OrderedDict): + if not isinstance(mapping, SortedDict): mapping.sort() for item_key, item_value in mapping: node_key = self.represent_data(item_key) @@ -102,7 +102,7 @@ def represent_mapping(self, tag, mapping, flow_style=None): SafeDumper.represent_decimal ) SafeDumper.add_representer( - OrderedDict, + SortedDict, yaml.representer.SafeRepresenter.represent_dict ) SafeDumper.add_representer( @@ -110,7 +110,7 @@ def represent_mapping(self, tag, mapping, flow_style=None): yaml.representer.SafeRepresenter.represent_dict ) SafeDumper.add_representer( - OrderedDictWithMetadata, + SortedDictWithMetadata, yaml.representer.SafeRepresenter.represent_dict ) SafeDumper.add_representer( diff --git a/rest_framework/views.py b/rest_framework/views.py index 526931e607..fb30775b19 100644 --- a/rest_framework/views.py +++ b/rest_framework/views.py @@ -7,7 +7,7 @@ from django.http import Http404 from django.views.decorators.csrf import csrf_exempt from rest_framework import status, exceptions -from rest_framework.compat import smart_text, HttpResponseBase, OrderedDict, View +from rest_framework.compat import smart_text, HttpResponseBase, SortedDict, View from rest_framework.request import Request from rest_framework.response import Response from rest_framework.settings import api_settings @@ -420,7 +420,7 @@ def metadata(self, request): # By default we can't provide any form-like information, however the # generic views override this implementation and add additional # information for POST and PUT methods, based on the serializer. - ret = OrderedDict() + ret = SortedDict() ret['name'] = self.get_view_name() ret['description'] = self.get_view_description() ret['renders'] = [renderer.media_type for renderer in self.renderer_classes] diff --git a/tests/test_serializer.py b/tests/test_serializer.py index c0de5cf274..14e88e6d23 100644 --- a/tests/test_serializer.py +++ b/tests/test_serializer.py @@ -1297,7 +1297,7 @@ def test_pickle_simple_model_serializer_data(self): def test_pickle_inner_serializer(self): """ - Test pickling a serializer whose resulting .data (a OrderedDictWithMetadata) will + Test pickling a serializer whose resulting .data (a SortedDictWithMetadata) will have unpickleable meta data--in order to make sure metadata doesn't get pulled into the pickle. See DictWithMetadata.__getstate__ """ @@ -1318,7 +1318,7 @@ def test_serializer_data_is_pickleable(self): """ Another regression test for #645. """ - data = serializers.OrderedDictWithMetadata({1: 1}) + data = serializers.SortedDictWithMetadata({1: 1}) repr(pickle.loads(pickle.dumps(data, 0))) From 73e433ed5c6835e413a2ebabffedb16bd36f437b Mon Sep 17 00:00:00 2001 From: kazmiruk Date: Thu, 23 Apr 2015 16:32:36 +0700 Subject: [PATCH 18/32] fixes tests for renamed SortedDict --- tests/test_fields.py | 8 ++------ tests/test_serializer.py | 2 +- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/tests/test_fields.py b/tests/test_fields.py index 261ef521cb..058ad70355 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -11,13 +11,9 @@ from django.db import models from django.test import TestCase from rest_framework import serializers +from rest_framework.compat import SortedDict from tests.models import RESTFrameworkModel -try: - from collections import OrderedDict -except ImportError: - from django.utils.datastructures import SortedDict as OrderedDict - class TimestampedModel(models.Model): added = models.DateTimeField(auto_now_add=True) @@ -99,7 +95,7 @@ def test_dict_field_ordering(self): Field should preserve dictionary ordering, if it exists. See: https://github.com/tomchristie/django-rest-framework/issues/832 """ - ret = OrderedDict() + ret = SortedDict() ret['c'] = 1 ret['b'] = 1 ret['a'] = 1 diff --git a/tests/test_serializer.py b/tests/test_serializer.py index 14e88e6d23..e72b723f05 100644 --- a/tests/test_serializer.py +++ b/tests/test_serializer.py @@ -1312,7 +1312,7 @@ def test_getstate_method_should_not_return_none(self): Regression test for #645. """ data = serializers.DictWithMetadata({1: 1}) - self.assertEqual(data.__getstate__(), serializers.OrderedDict({1: 1})) + self.assertEqual(data.__getstate__(), serializers.SortedDict({1: 1})) def test_serializer_data_is_pickleable(self): """ From e3522e8aef2b7dd1e93bde246672226f4243f644 Mon Sep 17 00:00:00 2001 From: kazmiruk Date: Fri, 24 Apr 2015 22:58:14 +0700 Subject: [PATCH 19/32] move importlib to compat --- rest_framework/compat.py | 8 ++++++++ rest_framework/settings.py | 6 +----- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/rest_framework/compat.py b/rest_framework/compat.py index 8532a01725..8979339c12 100644 --- a/rest_framework/compat.py +++ b/rest_framework/compat.py @@ -284,3 +284,11 @@ def python_2_unicode_compatible(klass): from django.contrib.contenttypes.fields import GenericForeignKey else: from django.contrib.contenttypes.generic import GenericForeignKey + +""" +django.utils.importlib is deprecated since django 1.8 +""" +try: + import importlib +except ImportError: + from django.utils import importlib diff --git a/rest_framework/settings.py b/rest_framework/settings.py index c7830b6658..e77ec754fc 100644 --- a/rest_framework/settings.py +++ b/rest_framework/settings.py @@ -21,11 +21,7 @@ from django.conf import settings from django.utils import six from rest_framework import ISO_8601 - -try: - import importlib -except ImportError: - from django.utils import importlib +from rest_framework.compat import importlib USER_SETTINGS = getattr(settings, 'REST_FRAMEWORK', None) From 7d8c95141cbbccd07f28406422dec11936714588 Mon Sep 17 00:00:00 2001 From: kazmiruk Date: Mon, 27 Apr 2015 10:02:05 +0700 Subject: [PATCH 20/32] remove list\tuple changes from PR --- rest_framework/serializers.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index 79984526db..e85100a1ea 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -992,15 +992,12 @@ def restore_object(self, attrs, instance=None): if field_name in attrs: m2m_data[field_name] = attrs.pop(field_name) - def _inner_loop_code(field): + # Forward m2m relations + for field in meta.many_to_many + meta.virtual_fields: if isinstance(field, GenericForeignKey): - return + continue if field.name in attrs: - m2m_data[field.name] = attrs.pop(field.name) - - # Forward m2m relations - [_inner_loop_code(field) for field in meta.many_to_many] - [_inner_loop_code(field) for field in meta.virtual_fields] + m2m_data[field.name] = attrs.pop(field.name) # Nested forward relations - These need to be marked so we can save # them before saving the parent model instance. From fad0848b7c544afff1cf4d91e23397fa8c9779a9 Mon Sep 17 00:00:00 2001 From: kazmiruk Date: Mon, 27 Apr 2015 10:09:43 +0700 Subject: [PATCH 21/32] fix flake8 warnings --- rest_framework/serializers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index e85100a1ea..0ce54731d4 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -997,7 +997,7 @@ def restore_object(self, attrs, instance=None): if isinstance(field, GenericForeignKey): continue if field.name in attrs: - m2m_data[field.name] = attrs.pop(field.name) + m2m_data[field.name] = attrs.pop(field.name) # Nested forward relations - These need to be marked so we can save # them before saving the parent model instance. From 86f7967a7a1d0dc1d9e02791526f6e2b8c60853f Mon Sep 17 00:00:00 2001 From: Xavier Ordoquy Date: Wed, 8 Jul 2015 08:49:59 +0200 Subject: [PATCH 22/32] Version 2.4.6 --- rest_framework/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rest_framework/__init__.py b/rest_framework/__init__.py index 5301909716..077bfb4cb8 100644 --- a/rest_framework/__init__.py +++ b/rest_framework/__init__.py @@ -8,7 +8,7 @@ """ __title__ = 'Django REST framework' -__version__ = '2.4.5' +__version__ = '2.4.6' __author__ = 'Tom Christie' __license__ = 'BSD 2-Clause' __copyright__ = 'Copyright 2011-2014 Tom Christie' From 152035aee73ae9d218a347cc966a4b9a69f4ed44 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 16 Jul 2015 16:46:07 +0100 Subject: [PATCH 23/32] Cherry picks Upgrade guardian support to 1.3. #3165 --- rest_framework/filters.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/rest_framework/filters.py b/rest_framework/filters.py index c580f9351b..2a05a8268a 100644 --- a/rest_framework/filters.py +++ b/rest_framework/filters.py @@ -188,4 +188,7 @@ def filter_queryset(self, request, queryset, view): 'model_name': get_model_name(model_cls) } permission = self.perm_format % kwargs - return guardian.shortcuts.get_objects_for_user(user, permission, queryset) + if guardian.VERSION >= (1, 3): + # Maintain behavior compatibility with versions prior to 1.3 + extra = {'accept_global_perms': False} + return guardian.shortcuts.get_objects_for_user(user, permission, queryset, **extra) From 59be95a0c0e81846833ab4acdc0938db45c49296 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Mon, 27 Jul 2015 10:20:37 +0100 Subject: [PATCH 24/32] Fix for DjangoObjectPermissionsFilter with Guardian < 1.3 --- rest_framework/filters.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rest_framework/filters.py b/rest_framework/filters.py index 2a05a8268a..18f4862e0f 100644 --- a/rest_framework/filters.py +++ b/rest_framework/filters.py @@ -191,4 +191,6 @@ def filter_queryset(self, request, queryset, view): if guardian.VERSION >= (1, 3): # Maintain behavior compatibility with versions prior to 1.3 extra = {'accept_global_perms': False} + else: + extra = {} return guardian.shortcuts.get_objects_for_user(user, permission, queryset, **extra) From 2c31f10f6e9216b62f64568898a9d4b4141e4954 Mon Sep 17 00:00:00 2001 From: Xavier Ordoquy Date: Sun, 16 Aug 2015 10:45:42 +0200 Subject: [PATCH 25/32] Release 2.4.7 --- docs/topics/release-notes.md | 9 +++++++++ rest_framework/__init__.py | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/docs/topics/release-notes.md b/docs/topics/release-notes.md index 9879c4665f..a45dddc7a3 100644 --- a/docs/topics/release-notes.md +++ b/docs/topics/release-notes.md @@ -40,6 +40,15 @@ You can determine your currently installed version using `pip freeze`: ## 2.4.x series +### 2.4.7 + +**Date**: [18 August 2015](https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%222.4.7+Release%22+) + +* Upgrade guardian support to 1.3 + + +### 2.4.6 + ### 2.4.5 **Date**: 24 March 2015 diff --git a/rest_framework/__init__.py b/rest_framework/__init__.py index 077bfb4cb8..d128482687 100644 --- a/rest_framework/__init__.py +++ b/rest_framework/__init__.py @@ -8,7 +8,7 @@ """ __title__ = 'Django REST framework' -__version__ = '2.4.6' +__version__ = '2.4.7' __author__ = 'Tom Christie' __license__ = 'BSD 2-Clause' __copyright__ = 'Copyright 2011-2014 Tom Christie' From 647fdb51d203976a82a9cde1c7a535bd89c73135 Mon Sep 17 00:00:00 2001 From: Xavier Ordoquy Date: Tue, 18 Aug 2015 07:59:28 +0200 Subject: [PATCH 26/32] Release 2.4.8 --- docs/topics/release-notes.md | 7 +++++++ rest_framework/__init__.py | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/docs/topics/release-notes.md b/docs/topics/release-notes.md index a45dddc7a3..682d57b130 100644 --- a/docs/topics/release-notes.md +++ b/docs/topics/release-notes.md @@ -40,6 +40,13 @@ You can determine your currently installed version using `pip freeze`: ## 2.4.x series +### 2.4.8 + +**Date**: 18 August 2015 + +* Repackage 2.4.7 without pyc files. + + ### 2.4.7 **Date**: [18 August 2015](https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%222.4.7+Release%22+) diff --git a/rest_framework/__init__.py b/rest_framework/__init__.py index d128482687..47ccda37a2 100644 --- a/rest_framework/__init__.py +++ b/rest_framework/__init__.py @@ -8,7 +8,7 @@ """ __title__ = 'Django REST framework' -__version__ = '2.4.7' +__version__ = '2.4.8' __author__ = 'Tom Christie' __license__ = 'BSD 2-Clause' __copyright__ = 'Copyright 2011-2014 Tom Christie' From 131e99baefaa935e90984246ada8b238e7208baa Mon Sep 17 00:00:00 2001 From: Sebastian Wozny Date: Mon, 31 Aug 2015 13:32:13 +0200 Subject: [PATCH 27/32] =?UTF-8?q?backport=20of=20#2492=20from=20tomchristi?= =?UTF-8?q?e=20ref=20#1850:=20=5Fclosable=5Fobjects=20as=20an=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- rest_framework/response.py | 1 + 1 file changed, 1 insertion(+) mode change 100644 => 100755 rest_framework/response.py diff --git a/rest_framework/response.py b/rest_framework/response.py old mode 100644 new mode 100755 index 0a7d313f40..ba48648c9d --- a/rest_framework/response.py +++ b/rest_framework/response.py @@ -91,4 +91,5 @@ def __getstate__(self): for key in ('accepted_renderer', 'renderer_context', 'data'): if key in state: del state[key] + state['_closable_objects'] = [] return state From 044fefa420c1a7bec89a63117f90f4c402906093 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Hernas?= Date: Tue, 8 Mar 2016 19:54:30 +0100 Subject: [PATCH 28/32] Fixes for Django 1.9 --- rest_framework/response.py | 5 ++--- rest_framework/serializers.py | 5 ++++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/rest_framework/response.py b/rest_framework/response.py index 0a7d313f40..3ec63f4033 100644 --- a/rest_framework/response.py +++ b/rest_framework/response.py @@ -6,10 +6,9 @@ """ from __future__ import unicode_literals import django -from django.core.handlers.wsgi import STATUS_CODE_TEXT from django.template.response import SimpleTemplateResponse from django.utils import six - +import httplib class Response(SimpleTemplateResponse): """ @@ -81,7 +80,7 @@ def status_text(self): """ # TODO: Deprecate and use a template tag instead # TODO: Status code text for RFC 6585 status codes - return STATUS_CODE_TEXT.get(self.status_code, '') + return httplib[self.status_code] def __getstate__(self): """ diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index aef815a888..c7a5de9872 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -1081,7 +1081,10 @@ def save_object(self, obj, **kwargs): self.save_object(related) else: # Reverse FK or reverse one-one - setattr(obj, accessor_name, related) + try: + setattr(obj, accessor_name, related) + except ValueError: + getattr(obj, accessor_name).add(*related, bulk=False) del(obj._related_data) From e26fe4356b453ef9b1c03000d380efcaadb936e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Hernas?= Date: Wed, 9 Mar 2016 16:03:56 +0100 Subject: [PATCH 29/32] Fixed comments --- .travis.yml | 3 ++- rest_framework/compat.py | 6 ++++++ rest_framework/response.py | 4 ++-- rest_framework/serializers.py | 8 ++++---- 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index a5b6d7d918..7dec96cf16 100644 --- a/.travis.yml +++ b/.travis.yml @@ -22,7 +22,8 @@ env: - TOX_ENV=py2.6-django1.4 install: - - "pip install tox --download-cache $HOME/.pip-cache" +# Virtualenv < 14 is required to keep the Python 3.2 builds running. + - "pip install tox 'virtualenv<14' --download-cache $HOME/.pip-cache" script: - tox -e $TOX_ENV diff --git a/rest_framework/compat.py b/rest_framework/compat.py index 8979339c12..b0150f3d58 100644 --- a/rest_framework/compat.py +++ b/rest_framework/compat.py @@ -73,6 +73,12 @@ from collections import UserDict from collections import MutableMapping as DictMixin +# http responses move in Python 3 +try: + from httplib import responses +except ImportError: + from http.client import responses + # Try to import PIL in either of the two ways it can end up installed. try: from PIL import Image diff --git a/rest_framework/response.py b/rest_framework/response.py index 3ec63f4033..27098d75b2 100644 --- a/rest_framework/response.py +++ b/rest_framework/response.py @@ -8,7 +8,7 @@ import django from django.template.response import SimpleTemplateResponse from django.utils import six -import httplib +from rest_framework.compat import responses class Response(SimpleTemplateResponse): """ @@ -80,7 +80,7 @@ def status_text(self): """ # TODO: Deprecate and use a template tag instead # TODO: Status code text for RFC 6585 status codes - return httplib[self.status_code] + return responses.get(self.status_code, '') def __getstate__(self): """ diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index c7a5de9872..29df9299d4 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -1079,12 +1079,12 @@ def save_object(self, obj, **kwargs): fk_field = obj._meta.get_field_by_name(accessor_name)[0].field.name setattr(related, fk_field, obj) self.save_object(related) + elif isinstance(related, list): + # Many to One/Many + getattr(obj, accessor_name).add(*related, bulk=False) else: # Reverse FK or reverse one-one - try: - setattr(obj, accessor_name, related) - except ValueError: - getattr(obj, accessor_name).add(*related, bulk=False) + setattr(obj, accessor_name, related) del(obj._related_data) From 604b9004c64bf6487ed8a7ed65e8fc86967bde5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Hernas?= Date: Wed, 9 Mar 2016 16:51:56 +0100 Subject: [PATCH 30/32] Fixed lint --- rest_framework/response.py | 1 + 1 file changed, 1 insertion(+) diff --git a/rest_framework/response.py b/rest_framework/response.py index 27098d75b2..10ac4efef1 100644 --- a/rest_framework/response.py +++ b/rest_framework/response.py @@ -10,6 +10,7 @@ from django.utils import six from rest_framework.compat import responses + class Response(SimpleTemplateResponse): """ An HttpResponse that allows its data to be rendered into From a6c73c6f90230fe9d3156f1f2f9a11e0a3057966 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Hernas?= Date: Wed, 9 Mar 2016 17:39:28 +0100 Subject: [PATCH 31/32] Check for Django version --- rest_framework/serializers.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index 29df9299d4..67cf432e09 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -15,6 +15,9 @@ import datetime import inspect import types + +import django + from decimal import Decimal from django.core.paginator import Page from django.db import models @@ -1081,7 +1084,10 @@ def save_object(self, obj, **kwargs): self.save_object(related) elif isinstance(related, list): # Many to One/Many - getattr(obj, accessor_name).add(*related, bulk=False) + if django.VERSION >= (1, 9): + getattr(obj, accessor_name).add(*related, bulk=False) + else: + getattr(obj, accessor_name).add(*related) else: # Reverse FK or reverse one-one setattr(obj, accessor_name, related) From 79d7021821063b2995b8e6a7000e04f0ed27ca21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Hernas?= Date: Thu, 17 Mar 2016 10:38:22 +0100 Subject: [PATCH 32/32] Bumped version --- rest_framework/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rest_framework/__init__.py b/rest_framework/__init__.py index 47ccda37a2..a15ed7cd49 100644 --- a/rest_framework/__init__.py +++ b/rest_framework/__init__.py @@ -8,7 +8,7 @@ """ __title__ = 'Django REST framework' -__version__ = '2.4.8' +__version__ = '2.4.9' __author__ = 'Tom Christie' __license__ = 'BSD 2-Clause' __copyright__ = 'Copyright 2011-2014 Tom Christie' pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy