mirror of
https://github.com/encode/django-rest-framework.git
synced 2025-08-04 20:40:14 +03:00
Merge ccf071f9a7
into 48b66ec2a2
This commit is contained in:
commit
0b79ebcb35
|
@ -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
|
||||||
|
|
|
@ -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',
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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':
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)))
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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]
|
||||||
|
|
8
setup.py
8
setup.py
|
@ -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',
|
||||||
|
|
Loading…
Reference in New Issue
Block a user