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.__unicode__ = klass.__str__
klass.__str__ = lambda self: self.__unicode__().encode('utf-8') klass.__str__ = lambda self: self.__unicode__().encode('utf-8')
return klass 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.forms import widgets
from django.utils.encoding import is_protected_type from django.utils.encoding import is_protected_type
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.utils.datastructures import SortedDict
from rest_framework import ISO_8601 from rest_framework import ISO_8601
from rest_framework.compat import ( from rest_framework.compat import (
timezone, parse_date, parse_datetime, parse_time, BytesIO, six, smart_text, 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 from rest_framework.settings import api_settings
@ -220,7 +219,7 @@ class Field(object):
return [self.to_native(item) for item in value] return [self.to_native(item) for item in value]
elif isinstance(value, dict): elif isinstance(value, dict):
# Make sure we preserve field ordering, if it exists # Make sure we preserve field ordering, if it exists
ret = SortedDict() ret = OrderedDict()
for key, val in value.items(): for key, val in value.items():
ret[key] = self.to_native(val) ret[key] = self.to_native(val)
return ret return ret
@ -235,7 +234,7 @@ class Field(object):
return {} return {}
def metadata(self): def metadata(self):
metadata = SortedDict() metadata = OrderedDict()
metadata['type'] = self.type_label metadata['type'] = self.type_label
metadata['required'] = getattr(self, 'required', False) metadata['required'] = getattr(self, 'required', False)
optional_attrs = ['read_only', 'label', 'help_text', 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.core.paginator import Page
from django.db import models from django.db import models
from django.forms import widgets from django.forms import widgets
from django.utils.datastructures import SortedDict
from django.core.exceptions import ObjectDoesNotExist 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 from rest_framework.settings import api_settings
@ -105,9 +104,9 @@ class DictWithMetadata(dict):
return dict(self) 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): def __getstate__(self):
""" """
@ -115,7 +114,7 @@ class SortedDictWithMetadata(SortedDict):
Overriden to remove the metadata from the dict, since it shouldn't be Overriden to remove the metadata from the dict, since it shouldn't be
pickle and may in some instances be unpickleable. pickle and may in some instances be unpickleable.
""" """
return SortedDict(self).__dict__ return OrderedDict(self).__dict__
def _is_protected_type(obj): def _is_protected_type(obj):
@ -151,7 +150,7 @@ def _get_declared_fields(bases, attrs):
if hasattr(base, 'base_fields'): if hasattr(base, 'base_fields'):
fields = list(base.base_fields.items()) + fields fields = list(base.base_fields.items()) + fields
return SortedDict(fields) return OrderedDict(fields)
class SerializerMetaclass(type): class SerializerMetaclass(type):
@ -179,7 +178,7 @@ class BaseSerializer(WritableField):
pass pass
_options_class = SerializerOptions _options_class = SerializerOptions
_dict_class = SortedDictWithMetadata _dict_class = OrderedDictWithMetadata
def __init__(self, instance=None, data=None, files=None, def __init__(self, instance=None, data=None, files=None,
context=None, partial=False, many=None, context=None, partial=False, many=None,
@ -225,7 +224,7 @@ class BaseSerializer(WritableField):
This will be the set of any explicitly declared fields, This will be the set of any explicitly declared fields,
plus the set of fields returned by get_default_fields(). plus the set of fields returned by get_default_fields().
""" """
ret = SortedDict() ret = OrderedDict()
# Get the explicitly declared fields # Get the explicitly declared fields
base_fields = copy.deepcopy(self.base_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 'fields' is specified, use those fields, in that order.
if self.opts.fields: if self.opts.fields:
assert isinstance(self.opts.fields, (list, tuple)), '`fields` must be a list or tuple' assert isinstance(self.opts.fields, (list, tuple)), '`fields` must be a list or tuple'
new = SortedDict() new = OrderedDict()
for key in self.opts.fields: for key in self.opts.fields:
new[key] = ret[key] new[key] = ret[key]
ret = new ret = new
@ -605,7 +604,7 @@ class BaseSerializer(WritableField):
Useful for things like responding to OPTIONS requests, or generating Useful for things like responding to OPTIONS requests, or generating
API schemas for auto-documentation. API schemas for auto-documentation.
""" """
return SortedDict( return OrderedDict(
[(field_name, field.metadata()) [(field_name, field.metadata())
for field_name, field in six.iteritems(self.fields)] for field_name, field in six.iteritems(self.fields)]
) )
@ -664,7 +663,7 @@ class ModelSerializer(Serializer):
assert cls is not None, \ assert cls is not None, \
"Serializer class '%s' is missing 'model' Meta option" % self.__class__.__name__ "Serializer class '%s' is missing 'model' Meta option" % self.__class__.__name__
opts = get_concrete_model(cls)._meta opts = get_concrete_model(cls)._meta
ret = SortedDict() ret = OrderedDict()
nested = bool(self.opts.depth) nested = bool(self.opts.depth)
# Deal with adding the primary key field # Deal with adding the primary key field

View File

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

View File

@ -10,8 +10,8 @@ from uuid import uuid4
from django.core import validators from django.core import validators
from django.db import models from django.db import models
from django.test import TestCase from django.test import TestCase
from django.utils.datastructures import SortedDict
from rest_framework import serializers from rest_framework import serializers
from rest_framework.compat import OrderedDict
from rest_framework.tests.models import RESTFrameworkModel from rest_framework.tests.models import RESTFrameworkModel
@ -95,7 +95,7 @@ class BasicFieldTests(TestCase):
Field should preserve dictionary ordering, if it exists. Field should preserve dictionary ordering, if it exists.
See: https://github.com/tomchristie/django-rest-framework/issues/832 See: https://github.com/tomchristie/django-rest-framework/issues/832
""" """
ret = SortedDict() ret = OrderedDict()
ret['c'] = 1 ret['c'] = 1
ret['b'] = 1 ret['b'] = 1
ret['a'] = 1 ret['a'] = 1

View File

@ -1274,7 +1274,7 @@ class SerializerPickleTests(TestCase):
def test_pickle_inner_serializer(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. have unpickleable meta data--in order to make sure metadata doesn't get pulled into the pickle.
See DictWithMetadata.__getstate__ See DictWithMetadata.__getstate__
""" """
@ -1289,13 +1289,13 @@ class SerializerPickleTests(TestCase):
Regression test for #645. Regression test for #645.
""" """
data = serializers.DictWithMetadata({1: 1}) 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): def test_serializer_data_is_pickleable(self):
""" """
Another regression test for #645. Another regression test for #645.
""" """
data = serializers.SortedDictWithMetadata({1: 1}) data = serializers.OrderedDictWithMetadata({1: 1})
repr(pickle.loads(pickle.dumps(data, 0))) repr(pickle.loads(pickle.dumps(data, 0)))

View File

@ -3,10 +3,9 @@ Helper classes for parsers.
""" """
from __future__ import unicode_literals from __future__ import unicode_literals
from django.db.models.query import QuerySet from django.db.models.query import QuerySet
from django.utils.datastructures import SortedDict
from django.utils.functional import Promise from django.utils.functional import Promise
from rest_framework.compat import timezone, force_text from rest_framework.compat import timezone, force_text, OrderedDict
from rest_framework.serializers import DictWithMetadata, SortedDictWithMetadata from rest_framework.serializers import DictWithMetadata, OrderedDictWithMetadata
import datetime import datetime
import decimal import decimal
import types import types
@ -66,7 +65,7 @@ else:
class SafeDumper(yaml.SafeDumper): class SafeDumper(yaml.SafeDumper):
""" """
Handles decimals as strings. 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. than the usual behaviour of sorting the keys.
""" """
def represent_decimal(self, data): def represent_decimal(self, data):
@ -80,7 +79,7 @@ else:
best_style = True best_style = True
if hasattr(mapping, 'items'): if hasattr(mapping, 'items'):
mapping = list(mapping.items()) mapping = list(mapping.items())
if not isinstance(mapping, SortedDict): if not isinstance(mapping, OrderedDict):
mapping.sort() mapping.sort()
for item_key, item_value in mapping: for item_key, item_value in mapping:
node_key = self.represent_data(item_key) node_key = self.represent_data(item_key)
@ -100,11 +99,11 @@ else:
SafeDumper.add_representer(decimal.Decimal, SafeDumper.add_representer(decimal.Decimal,
SafeDumper.represent_decimal) SafeDumper.represent_decimal)
SafeDumper.add_representer(SortedDict, SafeDumper.add_representer(OrderedDict,
yaml.representer.SafeRepresenter.represent_dict) yaml.representer.SafeRepresenter.represent_dict)
SafeDumper.add_representer(DictWithMetadata, SafeDumper.add_representer(DictWithMetadata,
yaml.representer.SafeRepresenter.represent_dict) yaml.representer.SafeRepresenter.represent_dict)
SafeDumper.add_representer(SortedDictWithMetadata, SafeDumper.add_representer(OrderedDictWithMetadata,
yaml.representer.SafeRepresenter.represent_dict) yaml.representer.SafeRepresenter.represent_dict)
SafeDumper.add_representer(types.GeneratorType, SafeDumper.add_representer(types.GeneratorType,
yaml.representer.SafeRepresenter.represent_list) yaml.representer.SafeRepresenter.represent_list)

View File

@ -5,10 +5,9 @@ from __future__ import unicode_literals
from django.core.exceptions import PermissionDenied from django.core.exceptions import PermissionDenied
from django.http import Http404 from django.http import Http404
from django.utils.datastructures import SortedDict
from django.views.decorators.csrf import csrf_exempt from django.views.decorators.csrf import csrf_exempt
from rest_framework import status, exceptions 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.request import Request
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.settings import api_settings 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 # By default we can't provide any form-like information, however the
# generic views override this implementation and add additional # generic views override this implementation and add additional
# information for POST and PUT methods, based on the serializer. # information for POST and PUT methods, based on the serializer.
ret = SortedDict() ret = OrderedDict()
ret['name'] = self.get_view_name() ret['name'] = self.get_view_name()
ret['description'] = self.get_view_description() ret['description'] = self.get_view_description()
ret['renders'] = [renderer.media_type for renderer in self.renderer_classes] 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") print(" git push --tags")
sys.exit() sys.exit()
requirements = []
try:
from collections import OrderedDict
except ImportError:
# Back-port for Python < 2.7
requirements.append('ordereddict')
setup( setup(
name='djangorestframework', name='djangorestframework',
@ -63,7 +69,7 @@ setup(
packages=get_packages('rest_framework'), packages=get_packages('rest_framework'),
package_data=get_package_data('rest_framework'), package_data=get_package_data('rest_framework'),
test_suite='rest_framework.runtests.runtests.main', test_suite='rest_framework.runtests.runtests.main',
install_requires=[], install_requires=requirements,
classifiers=[ classifiers=[
'Development Status :: 5 - Production/Stable', 'Development Status :: 5 - Production/Stable',
'Environment :: Web Environment', 'Environment :: Web Environment',