diff --git a/rest_framework/fields.py b/rest_framework/fields.py index f218713f1..4f59f891b 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -6,7 +6,6 @@ import inspect import re import uuid import warnings -from collections import OrderedDict from collections.abc import Mapping from django.conf import settings @@ -142,7 +141,7 @@ def to_choices_dict(choices): # choices = [1, 2, 3] # choices = [(1, 'First'), (2, 'Second'), (3, 'Third')] # choices = [('Category', ((1, 'First'), (2, 'Second'))), (3, 'Third')] - ret = OrderedDict() + ret = {} for choice in choices: if not isinstance(choice, (list, tuple)): # single choice @@ -165,7 +164,7 @@ def flatten_choices_dict(choices): flatten_choices_dict({1: '1st', 2: '2nd'}) -> {1: '1st', 2: '2nd'} flatten_choices_dict({'Group': {1: '1st', 2: '2nd'}}) -> {1: '1st', 2: '2nd'} """ - ret = OrderedDict() + ret = {} for key, value in choices.items(): if isinstance(value, dict): # grouped choices (category, sub choices) @@ -1662,7 +1661,7 @@ class ListField(Field): def run_child_validation(self, data): result = [] - errors = OrderedDict() + errors = {} for idx, item in enumerate(data): try: @@ -1724,7 +1723,7 @@ class DictField(Field): def run_child_validation(self, data): result = {} - errors = OrderedDict() + errors = {} for key, value in data.items(): key = str(key) diff --git a/rest_framework/metadata.py b/rest_framework/metadata.py index 8a44f2aad..99f448de9 100644 --- a/rest_framework/metadata.py +++ b/rest_framework/metadata.py @@ -6,8 +6,6 @@ some fairly ad-hoc information about the view. Future implementations might use JSON schema or other definitions in order to return this information in a more standardized way. """ -from collections import OrderedDict - from django.core.exceptions import PermissionDenied from django.http import Http404 from django.utils.encoding import force_str @@ -59,7 +57,7 @@ class SimpleMetadata(BaseMetadata): }) def determine_metadata(self, request, view): - metadata = OrderedDict() + metadata = {} metadata['name'] = view.get_view_name() metadata['description'] = view.get_view_description() metadata['renders'] = [renderer.media_type for renderer in view.renderer_classes] @@ -106,7 +104,7 @@ class SimpleMetadata(BaseMetadata): # If this is a `ListSerializer` then we want to examine the # underlying child serializer instance instead. serializer = serializer.child - return OrderedDict([ + return dict([ (field_name, self.get_field_info(field)) for field_name, field in serializer.fields.items() if not isinstance(field, serializers.HiddenField) @@ -117,7 +115,7 @@ class SimpleMetadata(BaseMetadata): Given an instance of a serializer field, return a dictionary of metadata about it. """ - field_info = OrderedDict() + field_info = {} field_info['type'] = self.label_lookup[field] field_info['required'] = getattr(field, 'required', False) diff --git a/rest_framework/pagination.py b/rest_framework/pagination.py index a73f2e526..32b862c6e 100644 --- a/rest_framework/pagination.py +++ b/rest_framework/pagination.py @@ -3,7 +3,7 @@ Pagination serializers determine the structure of the output that should be used for paginated responses. """ from base64 import b64decode, b64encode -from collections import OrderedDict, namedtuple +from collections import namedtuple from urllib import parse from django.core.paginator import InvalidPage @@ -218,12 +218,12 @@ class PageNumberPagination(BasePagination): return list(self.page) def get_paginated_response(self, data): - return Response(OrderedDict([ - ('count', self.page.paginator.count), - ('next', self.get_next_link()), - ('previous', self.get_previous_link()), - ('results', data) - ])) + return Response({ + 'count': self.page.paginator.count, + 'next': self.get_next_link(), + 'previous': self.get_previous_link(), + 'results': data, + }) def get_paginated_response_schema(self, schema): return { @@ -391,12 +391,12 @@ class LimitOffsetPagination(BasePagination): return list(queryset[self.offset:self.offset + self.limit]) def get_paginated_response(self, data): - return Response(OrderedDict([ - ('count', self.count), - ('next', self.get_next_link()), - ('previous', self.get_previous_link()), - ('results', data) - ])) + return Response({ + 'count': self.count, + 'next': self.get_next_link(), + 'previous': self.get_previous_link(), + 'results': data, + }) def get_paginated_response_schema(self, schema): return { @@ -889,11 +889,11 @@ class CursorPagination(BasePagination): return str(attr) def get_paginated_response(self, data): - return Response(OrderedDict([ - ('next', self.get_next_link()), - ('previous', self.get_previous_link()), - ('results', data) - ])) + return Response({ + 'next': self.get_next_link(), + 'previous': self.get_previous_link(), + 'results': data, + }) def get_paginated_response_schema(self, schema): return { diff --git a/rest_framework/relations.py b/rest_framework/relations.py index 3cd46379d..e1ad6d3b6 100644 --- a/rest_framework/relations.py +++ b/rest_framework/relations.py @@ -1,5 +1,4 @@ import sys -from collections import OrderedDict from urllib import parse from django.core.exceptions import ImproperlyConfigured, ObjectDoesNotExist @@ -199,7 +198,7 @@ class RelatedField(Field): if cutoff is not None: queryset = queryset[:cutoff] - return OrderedDict([ + return dict([ ( self.to_representation(item), self.display_value(item) diff --git a/rest_framework/renderers.py b/rest_framework/renderers.py index c790879b9..9ed0a73c8 100644 --- a/rest_framework/renderers.py +++ b/rest_framework/renderers.py @@ -7,7 +7,6 @@ on the response, such as JSON encoded data or HTML output. REST framework also provides an HTML renderer that renders the browsable API. """ import base64 -from collections import OrderedDict from urllib import parse from django import forms @@ -657,7 +656,7 @@ class BrowsableAPIRenderer(BaseRenderer): raw_data_patch_form = self.get_raw_data_form(data, view, 'PATCH', request) raw_data_put_or_patch_form = raw_data_put_form or raw_data_patch_form - response_headers = OrderedDict(sorted(response.items())) + response_headers = dict(sorted(response.items())) renderer_content_type = '' if renderer: renderer_content_type = '%s' % renderer.media_type diff --git a/rest_framework/routers.py b/rest_framework/routers.py index e2afa573f..878b73cb3 100644 --- a/rest_framework/routers.py +++ b/rest_framework/routers.py @@ -14,7 +14,7 @@ For example, you might have a `urls.py` that looks something like this: urlpatterns = router.urls """ import itertools -from collections import OrderedDict, namedtuple +from collections import namedtuple from django.core.exceptions import ImproperlyConfigured from django.urls import NoReverseMatch, re_path @@ -279,7 +279,7 @@ class APIRootView(views.APIView): def get(self, request, *args, **kwargs): # Return a plain {"name": "hyperlink"} response. - ret = OrderedDict() + ret = {} namespace = request.resolver_match.namespace for key, url_name in self.api_root_dict.items(): if namespace: @@ -323,7 +323,7 @@ class DefaultRouter(SimpleRouter): """ Return a basic root view. """ - api_root_dict = OrderedDict() + api_root_dict = {} list_name = self.routes[0].name for prefix, viewset, basename in self.registry: api_root_dict[prefix] = list_name.format(basename=basename) diff --git a/rest_framework/schemas/coreapi.py b/rest_framework/schemas/coreapi.py index 75ed5671a..681d330bd 100644 --- a/rest_framework/schemas/coreapi.py +++ b/rest_framework/schemas/coreapi.py @@ -1,5 +1,5 @@ import warnings -from collections import Counter, OrderedDict +from collections import Counter from urllib import parse from django.db import models @@ -54,7 +54,7 @@ to customise schema structure. """ -class LinkNode(OrderedDict): +class LinkNode(dict): def __init__(self): self.links = [] self.methods_counter = Counter() @@ -264,7 +264,7 @@ def field_to_schema(field): ) elif isinstance(field, serializers.Serializer): return coreschema.Object( - properties=OrderedDict([ + properties=dict([ (key, field_to_schema(value)) for key, value in field.fields.items() @@ -545,7 +545,7 @@ class AutoSchema(ViewInspector): if not update_with: return fields - by_name = OrderedDict((f.name, f) for f in fields) + by_name = dict((f.name, f) for f in fields) for f in update_with: by_name[f.name] = f fields = list(by_name.values()) diff --git a/rest_framework/schemas/openapi.py b/rest_framework/schemas/openapi.py index 8a8e267e0..8b499ddc1 100644 --- a/rest_framework/schemas/openapi.py +++ b/rest_framework/schemas/openapi.py @@ -1,6 +1,5 @@ import re import warnings -from collections import OrderedDict from decimal import Decimal from operator import attrgetter from urllib.parse import urljoin @@ -331,7 +330,7 @@ class AutoSchema(ViewInspector): return paginator.get_schema_operation_parameters(view) def map_choicefield(self, field): - choices = list(OrderedDict.fromkeys(field.choices)) # preserve order and remove duplicates + choices = list(dict.fromkeys(field.choices)) # preserve order and remove duplicates if all(isinstance(choice, bool) for choice in choices): type = 'boolean' elif all(isinstance(choice, int) for choice in choices): diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index 439220b34..b2775f7a8 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -13,7 +13,7 @@ response content is handled by parsers and renderers. import copy import inspect import traceback -from collections import OrderedDict, defaultdict +from collections import defaultdict from collections.abc import Mapping from django.core.exceptions import FieldDoesNotExist, ImproperlyConfigured @@ -302,7 +302,7 @@ class SerializerMetaclass(type): for name, f in base._declared_fields.items() if name not in known ] - return OrderedDict(base_fields + fields) + return dict(base_fields + fields) def __new__(cls, name, bases, attrs): attrs['_declared_fields'] = cls._get_declared_fields(bases, attrs) @@ -387,16 +387,16 @@ class Serializer(BaseSerializer, metaclass=SerializerMetaclass): if hasattr(self, 'initial_data'): # initial_data may not be a valid type if not isinstance(self.initial_data, Mapping): - return OrderedDict() + return {} - return OrderedDict([ + return dict([ (field_name, field.get_value(self.initial_data)) for field_name, field in self.fields.items() if (field.get_value(self.initial_data) is not empty) and not field.read_only ]) - return OrderedDict([ + return dict([ (field.field_name, field.get_initial()) for field in self.fields.values() if not field.read_only @@ -435,7 +435,7 @@ class Serializer(BaseSerializer, metaclass=SerializerMetaclass): if (field.read_only) and (field.default != empty) and (field.source != '*') and ('.' not in field.source) ] - defaults = OrderedDict() + defaults = {} for field in fields: try: default = field.get_default() @@ -468,8 +468,8 @@ class Serializer(BaseSerializer, metaclass=SerializerMetaclass): api_settings.NON_FIELD_ERRORS_KEY: [message] }, code='invalid') - ret = OrderedDict() - errors = OrderedDict() + ret = {} + errors = {} fields = self._writable_fields for field in fields: @@ -497,7 +497,7 @@ class Serializer(BaseSerializer, metaclass=SerializerMetaclass): """ Object instance -> Dict of primitive datatypes. """ - ret = OrderedDict() + ret = {} fields = self._readable_fields for field in fields: @@ -1040,7 +1040,7 @@ class ModelSerializer(Serializer): ) # Determine the fields that should be included on the serializer. - fields = OrderedDict() + fields = {} for field_name in field_names: # If the field is explicitly declared on the class then use that. @@ -1517,13 +1517,13 @@ class ModelSerializer(Serializer): # which may map onto a model field. Any dotted field name lookups # cannot map to a field, and must be a traversal, so we're not # including those. - field_sources = OrderedDict( + field_sources = dict( (field.field_name, field.source) for field in self._writable_fields if (field.source != '*') and ('.' not in field.source) ) # Special Case: Add read_only fields with defaults. - field_sources.update(OrderedDict( + field_sources.update(dict( (field.field_name, field.source) for field in self.fields.values() if (field.read_only) and (field.default != empty) and (field.source != '*') and ('.' not in field.source) )) diff --git a/rest_framework/templatetags/rest_framework.py b/rest_framework/templatetags/rest_framework.py index 79dd953ff..94a4bc358 100644 --- a/rest_framework/templatetags/rest_framework.py +++ b/rest_framework/templatetags/rest_framework.py @@ -1,5 +1,4 @@ import re -from collections import OrderedDict from django import template from django.template import loader @@ -49,7 +48,7 @@ def with_location(fields, location): @register.simple_tag def form_for_link(link): import coreschema - properties = OrderedDict([ + properties = dict([ (field.name, field.schema or coreschema.String()) for field in link.fields ]) @@ -272,7 +271,7 @@ def schema_links(section, sec_key=None): links.update(new_links) if sec_key is not None: - new_links = OrderedDict() + new_links = {} for link_key, link in links.items(): new_key = NESTED_FORMAT % (sec_key, link_key) new_links.update({new_key: link}) diff --git a/rest_framework/utils/model_meta.py b/rest_framework/utils/model_meta.py index 4cc93b8ef..60b325b15 100644 --- a/rest_framework/utils/model_meta.py +++ b/rest_framework/utils/model_meta.py @@ -5,7 +5,7 @@ relationships and their associated metadata. Usage: `get_field_info(model)` returns a `FieldInfo` instance. """ -from collections import OrderedDict, namedtuple +from collections import namedtuple FieldInfo = namedtuple('FieldResult', [ 'pk', # Model field instance @@ -58,7 +58,7 @@ def _get_pk(opts): def _get_fields(opts): - fields = OrderedDict() + fields = {} for field in [field for field in opts.fields if field.serialize and not field.remote_field]: fields[field.name] = field @@ -71,9 +71,9 @@ def _get_to_field(field): def _get_forward_relationships(opts): """ - Returns an `OrderedDict` of field names to `RelationInfo`. + Returns a `Dict` of field names to `RelationInfo`. """ - forward_relations = OrderedDict() + forward_relations = {} for field in [field for field in opts.fields if field.serialize and field.remote_field]: forward_relations[field.name] = RelationInfo( model_field=field, @@ -103,9 +103,9 @@ def _get_forward_relationships(opts): def _get_reverse_relationships(opts): """ - Returns an `OrderedDict` of field names to `RelationInfo`. + Returns a `Dict` of field names to `RelationInfo`. """ - reverse_relations = OrderedDict() + reverse_relations = {} all_related_objects = [r for r in opts.related_objects if not r.field.many_to_many] for relation in all_related_objects: accessor_name = relation.get_accessor_name() @@ -139,7 +139,7 @@ def _get_reverse_relationships(opts): def _merge_fields_and_pk(pk, fields): - fields_and_pk = OrderedDict() + fields_and_pk = {} fields_and_pk['pk'] = pk fields_and_pk[pk.name] = pk fields_and_pk.update(fields) @@ -148,7 +148,7 @@ def _merge_fields_and_pk(pk, fields): def _merge_relationships(forward_relations, reverse_relations): - return OrderedDict( + return dict( list(forward_relations.items()) + list(reverse_relations.items()) ) diff --git a/rest_framework/utils/serializer_helpers.py b/rest_framework/utils/serializer_helpers.py index b18fbe0df..b3a5ce6cf 100644 --- a/rest_framework/utils/serializer_helpers.py +++ b/rest_framework/utils/serializer_helpers.py @@ -1,4 +1,3 @@ -from collections import OrderedDict from collections.abc import MutableMapping from django.utils.encoding import force_str @@ -6,7 +5,7 @@ from django.utils.encoding import force_str from rest_framework.utils import json -class ReturnDict(OrderedDict): +class ReturnDict(dict): """ Return object from `serializer.data` for the `Serializer` class. Includes a backlink to the serializer instance for renderers @@ -138,7 +137,7 @@ class BindingDict(MutableMapping): def __init__(self, serializer): self.serializer = serializer - self.fields = OrderedDict() + self.fields = {} def __setitem__(self, key, field): self.fields[key] = field diff --git a/rest_framework/viewsets.py b/rest_framework/viewsets.py index 5a1f8acf5..073fca310 100644 --- a/rest_framework/viewsets.py +++ b/rest_framework/viewsets.py @@ -16,7 +16,6 @@ automatically. router.register(r'users', UserViewSet, 'user') urlpatterns = router.urls """ -from collections import OrderedDict from functools import update_wrapper from inspect import getmembers @@ -183,7 +182,7 @@ class ViewSetMixin: This method will noop if `detail` was not provided as a view initkwarg. """ - action_urls = OrderedDict() + action_urls = {} # exit early if `detail` has not been provided if self.detail is None: diff --git a/tests/test_model_serializer.py b/tests/test_model_serializer.py index 1733930a6..298ca851d 100644 --- a/tests/test_model_serializer.py +++ b/tests/test_model_serializer.py @@ -10,7 +10,6 @@ import decimal import json # noqa import sys import tempfile -from collections import OrderedDict import django import pytest @@ -778,7 +777,7 @@ class TestRelationalFieldDisplayValue(TestCase): fields = '__all__' serializer = TestSerializer() - expected = OrderedDict([(1, 'Red Color'), (2, 'Yellow Color'), (3, 'Green Color')]) + expected = {1: 'Red Color', 2: 'Yellow Color', 3: 'Green Color'} self.assertEqual(serializer.fields['color'].choices, expected) def test_custom_display_value(self): @@ -794,7 +793,7 @@ class TestRelationalFieldDisplayValue(TestCase): fields = '__all__' serializer = TestSerializer() - expected = OrderedDict([(1, 'My Red Color'), (2, 'My Yellow Color'), (3, 'My Green Color')]) + expected = {1: 'My Red Color', 2: 'My Yellow Color', 3: 'My Green Color'} self.assertEqual(serializer.fields['color'].choices, expected) diff --git a/tests/test_renderers.py b/tests/test_renderers.py index 71c5fb3f6..9709d357f 100644 --- a/tests/test_renderers.py +++ b/tests/test_renderers.py @@ -1,5 +1,4 @@ import re -from collections import OrderedDict from collections.abc import MutableMapping import pytest @@ -457,12 +456,12 @@ class CacheRenderTest(TestCase): class TestJSONIndentationStyles: def test_indented(self): renderer = JSONRenderer() - data = OrderedDict([('a', 1), ('b', 2)]) + data = {'a': 1, 'b': 2} assert renderer.render(data) == b'{"a":1,"b":2}' def test_compact(self): renderer = JSONRenderer() - data = OrderedDict([('a', 1), ('b', 2)]) + data = {'a': 1, 'b': 2} context = {'indent': 4} assert ( renderer.render(data, renderer_context=context) == @@ -472,7 +471,7 @@ class TestJSONIndentationStyles: def test_long_form(self): renderer = JSONRenderer() renderer.compact = False - data = OrderedDict([('a', 1), ('b', 2)]) + data = {'a': 1, 'b': 2} assert renderer.render(data) == b'{"a": 1, "b": 2}' diff --git a/tests/test_viewsets.py b/tests/test_viewsets.py index 2a2997a0b..0f8d653cd 100644 --- a/tests/test_viewsets.py +++ b/tests/test_viewsets.py @@ -1,4 +1,3 @@ -from collections import OrderedDict from functools import wraps import pytest @@ -261,11 +260,11 @@ class GetExtraActionUrlMapTests(TestCase): response = self.client.get('/api/actions/') view = response.view - expected = OrderedDict([ - ('Custom list action', 'http://testserver/api/actions/custom_list_action/'), - ('List action', 'http://testserver/api/actions/list_action/'), - ('Wrapped list action', 'http://testserver/api/actions/wrapped_list_action/'), - ]) + expected = { + 'Custom list action': 'http://testserver/api/actions/custom_list_action/', + 'List action': 'http://testserver/api/actions/list_action/', + 'Wrapped list action': 'http://testserver/api/actions/wrapped_list_action/', + } self.assertEqual(view.get_extra_action_url_map(), expected) @@ -273,28 +272,28 @@ class GetExtraActionUrlMapTests(TestCase): response = self.client.get('/api/actions/1/') view = response.view - expected = OrderedDict([ - ('Custom detail action', 'http://testserver/api/actions/1/custom_detail_action/'), - ('Detail action', 'http://testserver/api/actions/1/detail_action/'), - ('Wrapped detail action', 'http://testserver/api/actions/1/wrapped_detail_action/'), + expected = { + 'Custom detail action': 'http://testserver/api/actions/1/custom_detail_action/', + 'Detail action': 'http://testserver/api/actions/1/detail_action/', + 'Wrapped detail action': 'http://testserver/api/actions/1/wrapped_detail_action/', # "Unresolvable detail action" excluded, since it's not resolvable - ]) + } self.assertEqual(view.get_extra_action_url_map(), expected) def test_uninitialized_view(self): - self.assertEqual(ActionViewSet().get_extra_action_url_map(), OrderedDict()) + self.assertEqual(ActionViewSet().get_extra_action_url_map(), dict()) def test_action_names(self): # Action 'name' and 'suffix' kwargs should be respected response = self.client.get('/api/names/1/') view = response.view - expected = OrderedDict([ - ('Custom Name', 'http://testserver/api/names/1/named_action/'), - ('Action Names Custom Suffix', 'http://testserver/api/names/1/suffixed_action/'), - ('Unnamed action', 'http://testserver/api/names/1/unnamed_action/'), - ]) + expected = { + 'Custom Name': 'http://testserver/api/names/1/named_action/', + 'Action Names Custom Suffix': 'http://testserver/api/names/1/suffixed_action/', + 'Unnamed action': 'http://testserver/api/names/1/unnamed_action/', + } self.assertEqual(view.get_extra_action_url_map(), expected)