This commit is contained in:
David Fischer 2014-08-20 07:27:07 +00:00
commit 0b79ebcb35
9 changed files with 48 additions and 33 deletions

View File

@ -603,3 +603,12 @@ except ImportError:
klass.__unicode__ = klass.__str__
klass.__str__ = lambda self: self.__unicode__().encode('utf-8')
return klass
if django.VERSION >= (1, 7):
try:
from collections import OrderedDict
except ImportError:
# Fall-back to OrderedDict from ordereddict module
from ordereddict import OrderedDict
else:
from django.utils.datastructures import SortedDict as OrderedDict

View File

@ -20,11 +20,10 @@ from django.http import QueryDict
from django.forms import widgets
from django.utils.encoding import is_protected_type
from django.utils.translation import ugettext_lazy as _
from django.utils.datastructures import SortedDict
from rest_framework import ISO_8601
from rest_framework.compat import (
timezone, parse_date, parse_datetime, parse_time, BytesIO, six, smart_text,
force_text, is_non_str_iterable
force_text, is_non_str_iterable, OrderedDict
)
from rest_framework.settings import api_settings
@ -220,7 +219,7 @@ class Field(object):
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 = OrderedDict()
for key, val in value.items():
ret[key] = self.to_native(val)
return ret
@ -235,7 +234,7 @@ class Field(object):
return {}
def metadata(self):
metadata = SortedDict()
metadata = OrderedDict()
metadata['type'] = self.type_label
metadata['required'] = getattr(self, 'required', False)
optional_attrs = ['read_only', 'label', 'help_text',

View File

@ -20,9 +20,8 @@ from django.contrib.contenttypes.generic import GenericForeignKey
from django.core.paginator import Page
from django.db import models
from django.forms import widgets
from django.utils.datastructures import SortedDict
from django.core.exceptions import ObjectDoesNotExist
from rest_framework.compat import get_concrete_model, six
from rest_framework.compat import get_concrete_model, OrderedDict, six
from rest_framework.settings import api_settings
@ -105,9 +104,9 @@ class DictWithMetadata(dict):
return dict(self)
class SortedDictWithMetadata(SortedDict):
class OrderedDictWithMetadata(OrderedDict):
"""
A sorted dict-like object, that can have additional properties attached.
An ordered dict-like object, that can have additional properties attached.
"""
def __getstate__(self):
"""
@ -115,7 +114,7 @@ class SortedDictWithMetadata(SortedDict):
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):
@ -151,7 +150,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):
@ -179,7 +178,7 @@ class BaseSerializer(WritableField):
pass
_options_class = SerializerOptions
_dict_class = SortedDictWithMetadata
_dict_class = OrderedDictWithMetadata
def __init__(self, instance=None, data=None, files=None,
context=None, partial=False, many=None,
@ -225,7 +224,7 @@ class BaseSerializer(WritableField):
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)
@ -241,7 +240,7 @@ class BaseSerializer(WritableField):
# 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
@ -605,7 +604,7 @@ class BaseSerializer(WritableField):
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)]
)
@ -664,7 +663,7 @@ class ModelSerializer(Serializer):
assert cls is not None, \
"Serializer class '%s' is missing 'model' Meta option" % self.__class__.__name__
opts = get_concrete_model(cls)._meta
ret = SortedDict()
ret = OrderedDict()
nested = bool(self.opts.depth)
# Deal with adding the primary key field

View File

@ -20,6 +20,10 @@ back to the defaults.
from __future__ import unicode_literals
from django.conf import settings
try:
# Available in Python 2.7+
import importlib
except ImportError:
from django.utils import importlib
from rest_framework import ISO_8601
@ -51,7 +55,7 @@ DEFAULTS = {
'DEFAULT_CONTENT_NEGOTIATION_CLASS':
'rest_framework.negotiation.DefaultContentNegotiation',
# Genric view behavior
# Generic view behavior
'DEFAULT_MODEL_SERIALIZER_CLASS':
'rest_framework.serializers.ModelSerializer',
'DEFAULT_PAGINATION_SERIALIZER_CLASS':

View File

@ -10,8 +10,8 @@ from uuid import uuid4
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 rest_framework.compat import OrderedDict
from rest_framework.tests.models import RESTFrameworkModel
@ -95,7 +95,7 @@ class BasicFieldTests(TestCase):
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

View File

@ -1274,7 +1274,7 @@ class SerializerPickleTests(TestCase):
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__
"""
@ -1289,13 +1289,13 @@ class SerializerPickleTests(TestCase):
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)))

View File

@ -3,10 +3,9 @@ Helper classes for parsers.
"""
from __future__ import unicode_literals
from django.db.models.query import QuerySet
from django.utils.datastructures import SortedDict
from django.utils.functional import Promise
from rest_framework.compat import timezone, force_text
from rest_framework.serializers import DictWithMetadata, SortedDictWithMetadata
from rest_framework.compat import timezone, force_text, OrderedDict
from rest_framework.serializers import DictWithMetadata, OrderedDictWithMetadata
import datetime
import decimal
import types
@ -66,7 +65,7 @@ else:
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):
@ -80,7 +79,7 @@ else:
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)
@ -100,11 +99,11 @@ else:
SafeDumper.add_representer(decimal.Decimal,
SafeDumper.represent_decimal)
SafeDumper.add_representer(SortedDict,
SafeDumper.add_representer(OrderedDict,
yaml.representer.SafeRepresenter.represent_dict)
SafeDumper.add_representer(DictWithMetadata,
yaml.representer.SafeRepresenter.represent_dict)
SafeDumper.add_representer(SortedDictWithMetadata,
SafeDumper.add_representer(OrderedDictWithMetadata,
yaml.representer.SafeRepresenter.represent_dict)
SafeDumper.add_representer(types.GeneratorType,
yaml.representer.SafeRepresenter.represent_list)

View File

@ -5,10 +5,9 @@ from __future__ import unicode_literals
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
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
@ -418,7 +417,7 @@ class APIView(View):
# 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]

View File

@ -51,6 +51,12 @@ if sys.argv[-1] == 'publish':
print(" git push --tags")
sys.exit()
requirements = []
try:
from collections import OrderedDict
except ImportError:
# Back-port for Python < 2.7
requirements.append('ordereddict')
setup(
name='djangorestframework',
@ -63,7 +69,7 @@ setup(
packages=get_packages('rest_framework'),
package_data=get_package_data('rest_framework'),
test_suite='rest_framework.runtests.runtests.main',
install_requires=[],
install_requires=requirements,
classifiers=[
'Development Status :: 5 - Production/Stable',
'Environment :: Web Environment',