Jelajahi Sumber

Fixed #30159 -- Removed unneeded use of OrderedDict.

Dicts preserve order since Python 3.6.
Nick Pope 6 tahun lalu
induk
melakukan
24b82cd201

+ 8 - 7
django/apps/registry.py

@@ -2,7 +2,7 @@ import functools
 import sys
 import threading
 import warnings
-from collections import Counter, OrderedDict, defaultdict
+from collections import Counter, defaultdict
 from functools import partial
 
 from django.core.exceptions import AppRegistryNotReady, ImproperlyConfigured
@@ -31,10 +31,10 @@ class Apps:
         # and whether the registry has been populated. Since it isn't possible
         # to reimport a module safely (it could reexecute initialization code)
         # all_models is never overridden or reset.
-        self.all_models = defaultdict(OrderedDict)
+        self.all_models = defaultdict(dict)
 
         # Mapping of labels to AppConfig instances for installed apps.
-        self.app_configs = OrderedDict()
+        self.app_configs = {}
 
         # Stack of app_configs. Used to store the current state in
         # set_available_apps and set_installed_apps.
@@ -316,10 +316,11 @@ class Apps:
             )
 
         self.stored_app_configs.append(self.app_configs)
-        self.app_configs = OrderedDict(
-            (label, app_config)
+        self.app_configs = {
+            label: app_config
             for label, app_config in self.app_configs.items()
-            if app_config.name in available)
+            if app_config.name in available
+        }
         self.clear_cache()
 
     def unset_available_apps(self):
@@ -347,7 +348,7 @@ class Apps:
         if not self.ready:
             raise AppRegistryNotReady("App registry isn't ready yet.")
         self.stored_app_configs.append(self.app_configs)
-        self.app_configs = OrderedDict()
+        self.app_configs = {}
         self.apps_ready = self.models_ready = self.loading = self.ready = False
         self.clear_cache()
         self.populate(installed)

+ 3 - 11
django/contrib/admin/options.py

@@ -2,7 +2,6 @@ import copy
 import json
 import operator
 import re
-from collections import OrderedDict
 from functools import partial, reduce, update_wrapper
 from urllib.parse import quote as urlquote
 
@@ -682,10 +681,7 @@ class ModelAdmin(BaseModelAdmin):
         exclude = exclude or None
 
         # Remove declared form fields which are in readonly_fields.
-        new_attrs = OrderedDict.fromkeys(
-            f for f in readonly_fields
-            if f in self.form.declared_fields
-        )
+        new_attrs = dict.fromkeys(f for f in readonly_fields if f in self.form.declared_fields)
         form = type(self.form.__name__, (self.form,), new_attrs)
 
         defaults = {
@@ -886,13 +882,9 @@ class ModelAdmin(BaseModelAdmin):
         # If self.actions is set to None that means actions are disabled on
         # this page.
         if self.actions is None or IS_POPUP_VAR in request.GET:
-            return OrderedDict()
+            return {}
         actions = self._filter_actions_by_permissions(request, self._get_base_actions())
-        # Convert the actions into an OrderedDict keyed by name.
-        return OrderedDict(
-            (name, (func, name, desc))
-            for func, name, desc in actions
-        )
+        return {name: (func, name, desc) for func, name, desc in actions}
 
     def get_action_choices(self, request, default_choices=BLANK_CHOICE_DASH):
         """

+ 2 - 3
django/contrib/admin/views/main.py

@@ -1,4 +1,3 @@
-from collections import OrderedDict
 from datetime import datetime, timedelta
 
 from django.conf import settings
@@ -361,12 +360,12 @@ class ChangeList:
 
     def get_ordering_field_columns(self):
         """
-        Return an OrderedDict of ordering field column numbers and asc/desc.
+        Return a dictionary of ordering field column numbers and asc/desc.
         """
         # We must cope with more than one column having the same underlying sort
         # field, so we base things on column numbers.
         ordering = self._get_default_ordering()
-        ordering_fields = OrderedDict()
+        ordering_fields = {}
         if ORDER_VAR not in self.params:
             # for ordering specified on ModelAdmin or model Meta, we don't know
             # the right column numbers absolutely, because there might be more

+ 45 - 46
django/contrib/auth/hashers.py

@@ -4,7 +4,6 @@ import functools
 import hashlib
 import importlib
 import warnings
-from collections import OrderedDict
 
 from django.conf import settings
 from django.core.exceptions import ImproperlyConfigured
@@ -256,12 +255,12 @@ class PBKDF2PasswordHasher(BasePasswordHasher):
     def safe_summary(self, encoded):
         algorithm, iterations, salt, hash = encoded.split('$', 3)
         assert algorithm == self.algorithm
-        return OrderedDict([
-            (_('algorithm'), algorithm),
-            (_('iterations'), iterations),
-            (_('salt'), mask_hash(salt)),
-            (_('hash'), mask_hash(hash)),
-        ])
+        return {
+            _('algorithm'): algorithm,
+            _('iterations'): iterations,
+            _('salt'): mask_hash(salt),
+            _('hash'): mask_hash(hash),
+        }
 
     def must_update(self, encoded):
         algorithm, iterations, salt, hash = encoded.split('$', 3)
@@ -330,16 +329,16 @@ class Argon2PasswordHasher(BasePasswordHasher):
         (algorithm, variety, version, time_cost, memory_cost, parallelism,
             salt, data) = self._decode(encoded)
         assert algorithm == self.algorithm
-        return OrderedDict([
-            (_('algorithm'), algorithm),
-            (_('variety'), variety),
-            (_('version'), version),
-            (_('memory cost'), memory_cost),
-            (_('time cost'), time_cost),
-            (_('parallelism'), parallelism),
-            (_('salt'), mask_hash(salt)),
-            (_('hash'), mask_hash(data)),
-        ])
+        return {
+            _('algorithm'): algorithm,
+            _('variety'): variety,
+            _('version'): version,
+            _('memory cost'): memory_cost,
+            _('time cost'): time_cost,
+            _('parallelism'): parallelism,
+            _('salt'): mask_hash(salt),
+            _('hash'): mask_hash(data),
+        }
 
     def must_update(self, encoded):
         (algorithm, variety, version, time_cost, memory_cost, parallelism,
@@ -426,12 +425,12 @@ class BCryptSHA256PasswordHasher(BasePasswordHasher):
         algorithm, empty, algostr, work_factor, data = encoded.split('$', 4)
         assert algorithm == self.algorithm
         salt, checksum = data[:22], data[22:]
-        return OrderedDict([
-            (_('algorithm'), algorithm),
-            (_('work factor'), work_factor),
-            (_('salt'), mask_hash(salt)),
-            (_('checksum'), mask_hash(checksum)),
-        ])
+        return {
+            _('algorithm'): algorithm,
+            _('work factor'): work_factor,
+            _('salt'): mask_hash(salt),
+            _('checksum'): mask_hash(checksum),
+        }
 
     def must_update(self, encoded):
         algorithm, empty, algostr, rounds, data = encoded.split('$', 4)
@@ -486,11 +485,11 @@ class SHA1PasswordHasher(BasePasswordHasher):
     def safe_summary(self, encoded):
         algorithm, salt, hash = encoded.split('$', 2)
         assert algorithm == self.algorithm
-        return OrderedDict([
-            (_('algorithm'), algorithm),
-            (_('salt'), mask_hash(salt, show=2)),
-            (_('hash'), mask_hash(hash)),
-        ])
+        return {
+            _('algorithm'): algorithm,
+            _('salt'): mask_hash(salt, show=2),
+            _('hash'): mask_hash(hash),
+        }
 
     def harden_runtime(self, password, encoded):
         pass
@@ -517,11 +516,11 @@ class MD5PasswordHasher(BasePasswordHasher):
     def safe_summary(self, encoded):
         algorithm, salt, hash = encoded.split('$', 2)
         assert algorithm == self.algorithm
-        return OrderedDict([
-            (_('algorithm'), algorithm),
-            (_('salt'), mask_hash(salt, show=2)),
-            (_('hash'), mask_hash(hash)),
-        ])
+        return {
+            _('algorithm'): algorithm,
+            _('salt'): mask_hash(salt, show=2),
+            _('hash'): mask_hash(hash),
+        }
 
     def harden_runtime(self, password, encoded):
         pass
@@ -553,10 +552,10 @@ class UnsaltedSHA1PasswordHasher(BasePasswordHasher):
     def safe_summary(self, encoded):
         assert encoded.startswith('sha1$$')
         hash = encoded[6:]
-        return OrderedDict([
-            (_('algorithm'), self.algorithm),
-            (_('hash'), mask_hash(hash)),
-        ])
+        return {
+            _('algorithm'): self.algorithm,
+            _('hash'): mask_hash(hash),
+        }
 
     def harden_runtime(self, password, encoded):
         pass
@@ -589,10 +588,10 @@ class UnsaltedMD5PasswordHasher(BasePasswordHasher):
         return constant_time_compare(encoded, encoded_2)
 
     def safe_summary(self, encoded):
-        return OrderedDict([
-            (_('algorithm'), self.algorithm),
-            (_('hash'), mask_hash(encoded, show=3)),
-        ])
+        return {
+            _('algorithm'): self.algorithm,
+            _('hash'): mask_hash(encoded, show=3),
+        }
 
     def harden_runtime(self, password, encoded):
         pass
@@ -627,11 +626,11 @@ class CryptPasswordHasher(BasePasswordHasher):
     def safe_summary(self, encoded):
         algorithm, salt, data = encoded.split('$', 2)
         assert algorithm == self.algorithm
-        return OrderedDict([
-            (_('algorithm'), algorithm),
-            (_('salt'), salt),
-            (_('hash'), mask_hash(data, show=3)),
-        ])
+        return {
+            _('algorithm'): algorithm,
+            _('salt'): salt,
+            _('hash'): mask_hash(data, show=3),
+        }
 
     def harden_runtime(self, password, encoded):
         pass

+ 2 - 3
django/contrib/staticfiles/finders.py

@@ -1,6 +1,5 @@
 import functools
 import os
-from collections import OrderedDict
 
 from django.apps import apps
 from django.conf import settings
@@ -54,7 +53,7 @@ class FileSystemFinder(BaseFinder):
         # List of locations with static files
         self.locations = []
         # Maps dir paths to an appropriate storage instance
-        self.storages = OrderedDict()
+        self.storages = {}
         for root in settings.STATICFILES_DIRS:
             if isinstance(root, (list, tuple)):
                 prefix, root = root
@@ -144,7 +143,7 @@ class AppDirectoriesFinder(BaseFinder):
         # The list of apps that are handled
         self.apps = []
         # Mapping of app names to storage instances
-        self.storages = OrderedDict()
+        self.storages = {}
         app_configs = apps.get_app_configs()
         if app_names:
             app_names = set(app_names)

+ 1 - 2
django/contrib/staticfiles/management/commands/collectstatic.py

@@ -1,5 +1,4 @@
 import os
-from collections import OrderedDict
 
 from django.apps import apps
 from django.contrib.staticfiles.finders import get_finders
@@ -100,7 +99,7 @@ class Command(BaseCommand):
         else:
             handler = self.copy_file
 
-        found_files = OrderedDict()
+        found_files = {}
         for finder in get_finders():
             for path, storage in finder.list(self.ignore_patterns):
                 # Prefix the relative path if the source storage contains it

+ 7 - 8
django/contrib/staticfiles/storage.py

@@ -4,7 +4,6 @@ import os
 import posixpath
 import re
 import warnings
-from collections import OrderedDict
 from urllib.parse import unquote, urldefrag, urlsplit, urlunsplit
 
 from django.conf import settings
@@ -59,7 +58,7 @@ class HashedFilesMixin:
 
     def __init__(self, *args, **kwargs):
         super().__init__(*args, **kwargs)
-        self._patterns = OrderedDict()
+        self._patterns = {}
         self.hashed_files = {}
         for extension, patterns in self.patterns:
             for pattern in patterns:
@@ -208,7 +207,7 @@ class HashedFilesMixin:
 
     def post_process(self, paths, dry_run=False, **options):
         """
-        Post process the given OrderedDict of files (called from collectstatic).
+        Post process the given dictionary of files (called from collectstatic).
 
         Processing is actually two separate operations:
 
@@ -225,7 +224,7 @@ class HashedFilesMixin:
             return
 
         # where to store the new paths
-        hashed_files = OrderedDict()
+        hashed_files = {}
 
         # build a list of adjustable files
         adjustable_paths = [
@@ -386,20 +385,20 @@ class ManifestFilesMixin(HashedFilesMixin):
     def load_manifest(self):
         content = self.read_manifest()
         if content is None:
-            return OrderedDict()
+            return {}
         try:
-            stored = json.loads(content, object_pairs_hook=OrderedDict)
+            stored = json.loads(content)
         except json.JSONDecodeError:
             pass
         else:
             version = stored.get('version')
             if version == '1.0':
-                return stored.get('paths', OrderedDict())
+                return stored.get('paths', {})
         raise ValueError("Couldn't load manifest '%s' (version %s)" %
                          (self.manifest_name, self.manifest_version))
 
     def post_process(self, *args, **kwargs):
-        self.hashed_files = OrderedDict()
+        self.hashed_files = {}
         yield from super().post_process(*args, **kwargs)
         self.save_manifest()
 

+ 3 - 3
django/core/management/__init__.py

@@ -2,7 +2,7 @@ import functools
 import os
 import pkgutil
 import sys
-from collections import OrderedDict, defaultdict
+from collections import defaultdict
 from difflib import get_close_matches
 from importlib import import_module
 
@@ -339,8 +339,8 @@ class ManagementUtility:
                     # The exception will be raised later in the child process
                     # started by the autoreloader. Pretend it didn't happen by
                     # loading an empty list of applications.
-                    apps.all_models = defaultdict(OrderedDict)
-                    apps.app_configs = OrderedDict()
+                    apps.all_models = defaultdict(dict)
+                    apps.app_configs = {}
                     apps.apps_ready = apps.models_ready = apps.ready = True
 
                     # Remove options not compatible with the built-in runserver

+ 2 - 3
django/core/management/commands/dumpdata.py

@@ -1,5 +1,4 @@
 import warnings
-from collections import OrderedDict
 
 from django.apps import apps
 from django.core import serializers
@@ -87,14 +86,14 @@ class Command(BaseCommand):
         if not app_labels:
             if primary_keys:
                 raise CommandError("You can only use --pks option with one model")
-            app_list = OrderedDict.fromkeys(
+            app_list = dict.fromkeys(
                 app_config for app_config in apps.get_app_configs()
                 if app_config.models_module is not None and app_config not in excluded_apps
             )
         else:
             if len(app_labels) > 1 and primary_keys:
                 raise CommandError("You can only use --pks option with one model")
-            app_list = OrderedDict()
+            app_list = {}
             for label in app_labels:
                 try:
                     app_label, model_label = label.split('.')

+ 2 - 3
django/core/management/commands/inspectdb.py

@@ -1,6 +1,5 @@
 import keyword
 import re
-from collections import OrderedDict
 
 from django.core.management.base import BaseCommand, CommandError
 from django.db import DEFAULT_DB_ALIAS, connections
@@ -98,7 +97,7 @@ class Command(BaseCommand):
                 column_to_field_name = {}  # Maps column names to names of model fields
                 for row in table_description:
                     comment_notes = []  # Holds Field notes, to be displayed in a Python comment.
-                    extra_params = OrderedDict()  # Holds Field parameters such as 'db_column'.
+                    extra_params = {}  # Holds Field parameters such as 'db_column'.
                     column_name = row.name
                     is_relation = column_name in relations
 
@@ -232,7 +231,7 @@ class Command(BaseCommand):
         description, this routine will return the given field type name, as
         well as any additional keyword parameters and notes for the field.
         """
-        field_params = OrderedDict()
+        field_params = {}
         field_notes = []
 
         try:

+ 3 - 4
django/core/management/commands/migrate.py

@@ -1,5 +1,4 @@
 import time
-from collections import OrderedDict
 from importlib import import_module
 
 from django.apps import apps
@@ -314,10 +313,10 @@ class Command(BaseCommand):
                 (opts.auto_created and converter(opts.auto_created._meta.db_table) in tables)
             )
 
-        manifest = OrderedDict(
-            (app_name, list(filter(model_installed, model_list)))
+        manifest = {
+            app_name: list(filter(model_installed, model_list))
             for app_name, model_list in all_models
-        )
+        }
 
         # Create the tables for each model
         if self.verbosity >= 1:

+ 2 - 3
django/core/serializers/python.py

@@ -3,7 +3,6 @@ A Python "serializer". Doesn't do much serializing per se -- just converts to
 and from basic Python data types (lists, dicts, strings, etc.). Useful as a basis for
 other serializers.
 """
-from collections import OrderedDict
 
 from django.apps import apps
 from django.core.serializers import base
@@ -26,14 +25,14 @@ class Serializer(base.Serializer):
         pass
 
     def start_object(self, obj):
-        self._current = OrderedDict()
+        self._current = {}
 
     def end_object(self, obj):
         self.objects.append(self.get_dump_object(obj))
         self._current = None
 
     def get_dump_object(self, obj):
-        data = OrderedDict([('model', str(obj._meta))])
+        data = {'model': str(obj._meta)}
         if not self.use_natural_primary_keys or not hasattr(obj, 'natural_key'):
             data["pk"] = self._value_from_field(obj, obj._meta.pk)
         data['fields'] = self._current

+ 3 - 0
django/core/serializers/pyyaml.py

@@ -34,6 +34,9 @@ class DjangoSafeDumper(SafeDumper):
 
 DjangoSafeDumper.add_representer(decimal.Decimal, DjangoSafeDumper.represent_decimal)
 DjangoSafeDumper.add_representer(collections.OrderedDict, DjangoSafeDumper.represent_ordered_dict)
+# Workaround to represent dictionaries in insertion order.
+# See https://github.com/yaml/pyyaml/pull/143.
+DjangoSafeDumper.add_representer(dict, DjangoSafeDumper.represent_ordered_dict)
 
 
 class Serializer(PythonSerializer):

+ 20 - 20
django/db/migrations/serializer.py

@@ -8,7 +8,6 @@ import math
 import re
 import types
 import uuid
-from collections import OrderedDict
 
 from django.conf import SettingsReference
 from django.db import models
@@ -273,25 +272,26 @@ class UUIDSerializer(BaseSerializer):
 
 
 class Serializer:
-    _registry = OrderedDict([
-        (frozenset, FrozensetSerializer),
-        (list, SequenceSerializer),
-        (set, SetSerializer),
-        (tuple, TupleSerializer),
-        (dict, DictionarySerializer),
-        (enum.Enum, EnumSerializer),
-        (datetime.datetime, DatetimeDatetimeSerializer),
-        ((datetime.date, datetime.timedelta, datetime.time), DateTimeSerializer),
-        (SettingsReference, SettingsReferenceSerializer),
-        (float, FloatSerializer),
-        ((bool, int, type(None), bytes, str), BaseSimpleSerializer),
-        (decimal.Decimal, DecimalSerializer),
-        ((functools.partial, functools.partialmethod), FunctoolsPartialSerializer),
-        ((types.FunctionType, types.BuiltinFunctionType, types.MethodType), FunctionTypeSerializer),
-        (collections.abc.Iterable, IterableSerializer),
-        ((COMPILED_REGEX_TYPE, RegexObject), RegexSerializer),
-        (uuid.UUID, UUIDSerializer),
-    ])
+    _registry = {
+        # Some of these are order-dependent.
+        frozenset: FrozensetSerializer,
+        list: SequenceSerializer,
+        set: SetSerializer,
+        tuple: TupleSerializer,
+        dict: DictionarySerializer,
+        enum.Enum: EnumSerializer,
+        datetime.datetime: DatetimeDatetimeSerializer,
+        (datetime.date, datetime.timedelta, datetime.time): DateTimeSerializer,
+        SettingsReference: SettingsReferenceSerializer,
+        float: FloatSerializer,
+        (bool, int, type(None), bytes, str): BaseSimpleSerializer,
+        decimal.Decimal: DecimalSerializer,
+        (functools.partial, functools.partialmethod): FunctoolsPartialSerializer,
+        (types.FunctionType, types.BuiltinFunctionType, types.MethodType): FunctionTypeSerializer,
+        collections.abc.Iterable: IterableSerializer,
+        (COMPILED_REGEX_TYPE, RegexObject): RegexSerializer,
+        uuid.UUID: UUIDSerializer,
+    }
 
     @classmethod
     def register(cls, type_, serializer):

+ 1 - 2
django/db/migrations/state.py

@@ -1,5 +1,4 @@
 import copy
-from collections import OrderedDict
 from contextlib import contextmanager
 
 from django.apps import AppConfig
@@ -334,7 +333,7 @@ class StateApps(Apps):
         if app_label not in self.app_configs:
             self.app_configs[app_label] = AppConfigStub(app_label)
             self.app_configs[app_label].apps = self
-            self.app_configs[app_label].models = OrderedDict()
+            self.app_configs[app_label].models = {}
         self.app_configs[app_label].models[model._meta.model_name] = model
         self.do_pending_operations(model)
         self.clear_cache()

+ 3 - 4
django/db/models/deletion.py

@@ -1,4 +1,4 @@
-from collections import Counter, OrderedDict
+from collections import Counter
 from operator import attrgetter
 
 from django.db import IntegrityError, connections, transaction
@@ -64,7 +64,7 @@ class Collector:
     def __init__(self, using):
         self.using = using
         # Initially, {model: {instances}}, later values become lists.
-        self.data = OrderedDict()
+        self.data = {}
         self.field_updates = {}  # {model: {(field, value): {instances}}}
         # fast_deletes is a list of queryset-likes that can be deleted without
         # fetching the objects into memory.
@@ -257,8 +257,7 @@ class Collector:
                     found = True
             if not found:
                 return
-        self.data = OrderedDict((model, self.data[model])
-                                for model in sorted_models)
+        self.data = {model: self.data[model] for model in sorted_models}
 
     def delete(self):
         # sort instance collections

+ 2 - 2
django/db/models/options.py

@@ -1,7 +1,7 @@
 import copy
 import inspect
 from bisect import bisect
-from collections import OrderedDict, defaultdict
+from collections import defaultdict
 
 from django.apps import apps
 from django.conf import settings
@@ -117,7 +117,7 @@ class Options:
         # concrete models, the concrete_model is always the class itself.
         self.concrete_model = None
         self.swappable = None
-        self.parents = OrderedDict()
+        self.parents = {}
         self.auto_created = False
 
         # List of all lookups defined in ForeignKey 'limit_choices_to' options

+ 4 - 4
django/db/models/query.py

@@ -5,7 +5,7 @@ The main QuerySet implementation. This provides the public API for the ORM.
 import copy
 import operator
 import warnings
-from collections import OrderedDict, namedtuple
+from collections import namedtuple
 from functools import lru_cache
 from itertools import chain
 
@@ -725,7 +725,7 @@ class QuerySet:
         query = self.query.chain(sql.UpdateQuery)
         query.add_update_values(kwargs)
         # Clear any annotations so that they won't be present in subqueries.
-        query._annotations = None
+        query.annotations = {}
         with transaction.mark_for_rollback_on_error(using=self.db):
             rows = query.get_compiler(self.db).execute_sql(CURSOR)
         self._result_cache = None
@@ -744,7 +744,7 @@ class QuerySet:
         query = self.query.chain(sql.UpdateQuery)
         query.add_update_fields(values)
         # Clear any annotations so that they won't be present in subqueries.
-        query._annotations = None
+        query.annotations = {}
         self._result_cache = None
         return query.get_compiler(self.db).execute_sql(CURSOR)
     _update.alters_data = True
@@ -1014,7 +1014,7 @@ class QuerySet:
         with extra data or aggregations.
         """
         self._validate_values_are_expressions(args + tuple(kwargs.values()), method_name='annotate')
-        annotations = OrderedDict()  # To preserve ordering of args
+        annotations = {}
         for arg in args:
             # The default_alias property may raise a TypeError.
             try:

+ 2 - 2
django/db/models/sql/compiler.py

@@ -317,7 +317,7 @@ class SQLCompiler:
                     ), False))
                 continue
 
-            if not self.query._extra or col not in self.query._extra:
+            if not self.query.extra or col not in self.query.extra:
                 # 'col' is of the form 'field' or 'field1__field2' or
                 # '-field1__field2__field', etc.
                 order_by.extend(self.find_ordering_name(
@@ -1438,7 +1438,7 @@ class SQLUpdateCompiler(SQLCompiler):
         query = self.query.chain(klass=Query)
         query.select_related = False
         query.clear_ordering(True)
-        query._extra = {}
+        query.extra = {}
         query.select = []
         query.add_fields([query.get_meta().pk.name])
         super().pre_sql_setup()

+ 27 - 46
django/db/models/sql/query.py

@@ -8,7 +8,7 @@ all about the internals of models in order to get the information it needs.
 """
 import difflib
 import functools
-from collections import Counter, OrderedDict, namedtuple
+from collections import Counter, namedtuple
 from collections.abc import Iterator, Mapping
 from itertools import chain, count, product
 from string import ascii_uppercase
@@ -152,7 +152,7 @@ class Query:
         # types they are. The key is the alias of the joined table (possibly
         # the table name) and the value is a Join-like object (see
         # sql.datastructures.Join for more information).
-        self.alias_map = OrderedDict()
+        self.alias_map = {}
         # Sometimes the query contains references to aliases in outer queries (as
         # a result of split_exclude). Correct alias quoting needs to know these
         # aliases too.
@@ -199,10 +199,7 @@ class Query:
         self.values_select = ()
 
         # SQL annotation-related attributes
-        # The _annotations will be an OrderedDict when used. Due to the cost
-        # of creating OrderedDict this attribute is created lazily (in
-        # self.annotations property).
-        self._annotations = None  # Maps alias -> Annotation Expression
+        self.annotations = {}  # Maps alias -> Annotation Expression
         self.annotation_select_mask = None
         self._annotation_select_cache = None
 
@@ -213,9 +210,7 @@ class Query:
 
         # These are for extensions. The contents are more or less appended
         # verbatim to the appropriate clause.
-        # The _extra attribute is an OrderedDict, lazily created similarly to
-        # .annotations
-        self._extra = None  # Maps col_alias -> (col_sql, params).
+        self.extra = {}  # Maps col_alias -> (col_sql, params).
         self.extra_select_mask = None
         self._extra_select_cache = None
 
@@ -233,18 +228,6 @@ class Query:
         self.explain_format = None
         self.explain_options = {}
 
-    @property
-    def extra(self):
-        if self._extra is None:
-            self._extra = OrderedDict()
-        return self._extra
-
-    @property
-    def annotations(self):
-        if self._annotations is None:
-            self._annotations = OrderedDict()
-        return self._annotations
-
     @property
     def has_select_fields(self):
         return bool(self.select or self.annotation_select_mask or self.extra_select_mask)
@@ -311,7 +294,7 @@ class Query:
         obj.external_aliases = self.external_aliases.copy()
         obj.table_map = self.table_map.copy()
         obj.where = self.where.clone()
-        obj._annotations = self._annotations.copy() if self._annotations is not None else None
+        obj.annotations = self.annotations.copy()
         if self.annotation_select_mask is None:
             obj.annotation_select_mask = None
         else:
@@ -322,7 +305,7 @@ class Query:
         # It will get re-populated in the cloned queryset the next time it's
         # used.
         obj._annotation_select_cache = None
-        obj._extra = self._extra.copy() if self._extra is not None else None
+        obj.extra = self.extra.copy()
         if self.extra_select_mask is None:
             obj.extra_select_mask = None
         else:
@@ -479,7 +462,7 @@ class Query:
             outer_query = self
             self.select = ()
             self.default_cols = False
-            self._extra = {}
+            self.extra = {}
 
         outer_query.clear_ordering(True)
         outer_query.clear_limits()
@@ -613,7 +596,7 @@ class Query:
             # It would be nice to be able to handle this, but the queries don't
             # really make sense (or return consistent value sets). Not worth
             # the extra complexity when you can write a real query instead.
-            if self._extra and rhs._extra:
+            if self.extra and rhs.extra:
                 raise ValueError("When merging querysets using 'or', you cannot have extra(select=…) on both sides.")
         self.extra.update(rhs.extra)
         extra_select_mask = set()
@@ -825,9 +808,9 @@ class Query:
         if isinstance(self.group_by, tuple):
             self.group_by = tuple([col.relabeled_clone(change_map) for col in self.group_by])
         self.select = tuple([col.relabeled_clone(change_map) for col in self.select])
-        self._annotations = self._annotations and OrderedDict(
-            (key, col.relabeled_clone(change_map)) for key, col in self._annotations.items()
-        )
+        self.annotations = self.annotations and {
+            key: col.relabeled_clone(change_map) for key, col in self.annotations.items()
+        }
 
         # 2. Rename the alias in the internal table/alias datastructures.
         for old_alias, new_alias in change_map.items():
@@ -887,11 +870,10 @@ class Query:
                 )
         self.subq_aliases = self.subq_aliases.union([self.alias_prefix])
         outer_query.subq_aliases = outer_query.subq_aliases.union(self.subq_aliases)
-        change_map = OrderedDict()
-        for pos, alias in enumerate(self.alias_map):
-            new_alias = '%s%d' % (self.alias_prefix, pos)
-            change_map[alias] = new_alias
-        self.change_aliases(change_map)
+        self.change_aliases({
+            alias: '%s%d' % (self.alias_prefix, pos)
+            for pos, alias in enumerate(self.alias_map)
+        })
 
     def get_initial_alias(self):
         """
@@ -1042,7 +1024,7 @@ class Query:
         Solve the lookup type from the lookup (e.g.: 'foobar__id__icontains').
         """
         lookup_splitted = lookup.split(LOOKUP_SEP)
-        if self._annotations:
+        if self.annotations:
             expression, expression_lookups = refs_expression(lookup_splitted, self.annotations)
             if expression:
                 return expression_lookups, (), expression
@@ -1867,7 +1849,7 @@ class Query:
             # dictionary with their parameters in 'select_params' so that
             # subsequent updates to the select dictionary also adjust the
             # parameters appropriately.
-            select_pairs = OrderedDict()
+            select_pairs = {}
             if select_params:
                 param_iter = iter(select_params)
             else:
@@ -1881,7 +1863,6 @@ class Query:
                         entry_params.append(next(param_iter))
                     pos = entry.find("%s", pos + 2)
                 select_pairs[name] = (entry, entry_params)
-            # This is order preserving, since self.extra_select is an OrderedDict.
             self.extra.update(select_pairs)
         if where or params:
             self.where.add(ExtraWhere(where, params), AND)
@@ -1998,7 +1979,7 @@ class Query:
             field_names = []
             extra_names = []
             annotation_names = []
-            if not self._extra and not self._annotations:
+            if not self.extra and not self.annotations:
                 # Shortcut - if there are no extra or annotations, then
                 # the values() clause must be just field names.
                 field_names = list(fields)
@@ -2022,18 +2003,18 @@ class Query:
     @property
     def annotation_select(self):
         """
-        Return the OrderedDict of aggregate columns that are not masked and
+        Return the dictionary of aggregate columns that are not masked and
         should be used in the SELECT clause. Cache this result for performance.
         """
         if self._annotation_select_cache is not None:
             return self._annotation_select_cache
-        elif not self._annotations:
+        elif not self.annotations:
             return {}
         elif self.annotation_select_mask is not None:
-            self._annotation_select_cache = OrderedDict(
-                (k, v) for k, v in self.annotations.items()
+            self._annotation_select_cache = {
+                k: v for k, v in self.annotations.items()
                 if k in self.annotation_select_mask
-            )
+            }
             return self._annotation_select_cache
         else:
             return self.annotations
@@ -2042,13 +2023,13 @@ class Query:
     def extra_select(self):
         if self._extra_select_cache is not None:
             return self._extra_select_cache
-        if not self._extra:
+        if not self.extra:
             return {}
         elif self.extra_select_mask is not None:
-            self._extra_select_cache = OrderedDict(
-                (k, v) for k, v in self.extra.items()
+            self._extra_select_cache = {
+                k: v for k, v in self.extra.items()
                 if k in self.extra_select_mask
-            )
+            }
             return self._extra_select_cache
         else:
             return self.extra

+ 3 - 9
django/forms/forms.py

@@ -3,7 +3,6 @@ Form classes
 """
 
 import copy
-from collections import OrderedDict
 
 from django.core.exceptions import NON_FIELD_ERRORS, ValidationError
 # BoundField is imported for backwards compatibility in Django 1.9
@@ -31,12 +30,12 @@ class DeclarativeFieldsMetaclass(MediaDefiningClass):
             if isinstance(value, Field):
                 current_fields.append((key, value))
                 attrs.pop(key)
-        attrs['declared_fields'] = OrderedDict(current_fields)
+        attrs['declared_fields'] = dict(current_fields)
 
         new_class = super(DeclarativeFieldsMetaclass, mcs).__new__(mcs, name, bases, attrs)
 
         # Walk through the MRO.
-        declared_fields = OrderedDict()
+        declared_fields = {}
         for base in reversed(new_class.__mro__):
             # Collect fields from base class.
             if hasattr(base, 'declared_fields'):
@@ -52,11 +51,6 @@ class DeclarativeFieldsMetaclass(MediaDefiningClass):
 
         return new_class
 
-    @classmethod
-    def __prepare__(metacls, name, bases, **kwds):
-        # Remember the order in which form fields are defined.
-        return OrderedDict()
-
 
 @html_safe
 class BaseForm:
@@ -129,7 +123,7 @@ class BaseForm:
         """
         if field_order is None:
             return
-        fields = OrderedDict()
+        fields = {}
         for key in field_order:
             try:
                 fields[key] = self.fields.pop(key)

+ 7 - 9
django/forms/models.py

@@ -3,7 +3,6 @@ Helper functions for creating Form classes from Django models
 and database field objects.
 """
 
-from collections import OrderedDict
 from itertools import chain
 
 from django.core.exceptions import (
@@ -105,7 +104,7 @@ def fields_for_model(model, fields=None, exclude=None, widgets=None,
                      labels=None, help_texts=None, error_messages=None,
                      field_classes=None, *, apply_limit_choices_to=True):
     """
-    Return an ``OrderedDict`` containing form fields for the given model.
+    Return a dictionary containing form fields for the given model.
 
     ``fields`` is an optional list of field names. If provided, return only the
     named fields.
@@ -134,7 +133,7 @@ def fields_for_model(model, fields=None, exclude=None, widgets=None,
     ``apply_limit_choices_to`` is a boolean indicating if limit_choices_to
     should be applied to a field's queryset.
     """
-    field_list = []
+    field_dict = {}
     ignored = []
     opts = model._meta
     # Avoid circular import
@@ -178,15 +177,14 @@ def fields_for_model(model, fields=None, exclude=None, widgets=None,
         if formfield:
             if apply_limit_choices_to:
                 apply_limit_choices_to_to_formfield(formfield)
-            field_list.append((f.name, formfield))
+            field_dict[f.name] = formfield
         else:
             ignored.append(f.name)
-    field_dict = OrderedDict(field_list)
     if fields:
-        field_dict = OrderedDict(
-            [(f, field_dict.get(f)) for f in fields
-                if ((not exclude) or (exclude and f not in exclude)) and (f not in ignored)]
-        )
+        field_dict = {
+            f: field_dict.get(f) for f in fields
+            if (not exclude or f not in exclude) and f not in ignored
+        }
     return field_dict
 
 

+ 2 - 2
django/template/utils.py

@@ -1,5 +1,5 @@
 import functools
-from collections import Counter, OrderedDict
+from collections import Counter
 from pathlib import Path
 
 from django.apps import apps
@@ -27,7 +27,7 @@ class EngineHandler:
         if self._templates is None:
             self._templates = settings.TEMPLATES
 
-        templates = OrderedDict()
+        templates = {}
         backend_names = []
         for tpl in self._templates:
             try:

+ 1 - 3
django/test/utils.py

@@ -1,4 +1,3 @@
-import collections
 import logging
 import re
 import sys
@@ -280,8 +279,7 @@ def get_unique_databases_and_mirrors(aliases=None):
                 if alias != DEFAULT_DB_ALIAS and connection.creation.test_db_signature() != default_sig:
                     dependencies[alias] = test_settings.get('DEPENDENCIES', [DEFAULT_DB_ALIAS])
 
-    test_databases = dependency_ordered(test_databases.items(), dependencies)
-    test_databases = collections.OrderedDict(test_databases)
+    test_databases = dict(dependency_ordered(test_databases.items(), dependencies))
     return test_databases, mirrored_aliases
 
 

+ 1 - 3
django/utils/datastructures.py

@@ -1,16 +1,14 @@
 import copy
-from collections import OrderedDict
 from collections.abc import Mapping
 
 
 class OrderedSet:
     """
     A set which keeps the ordering of the inserted items.
-    Currently backs onto OrderedDict.
     """
 
     def __init__(self, iterable=None):
-        self.dict = OrderedDict.fromkeys(iterable or ())
+        self.dict = dict.fromkeys(iterable or ())
 
     def add(self, item):
         self.dict[item] = None

+ 2 - 3
django/utils/translation/trans_real.py

@@ -5,7 +5,6 @@ import os
 import re
 import sys
 import warnings
-from collections import OrderedDict
 from threading import local
 
 from django.apps import apps
@@ -385,9 +384,9 @@ def check_for_language(lang_code):
 @functools.lru_cache()
 def get_languages():
     """
-    Cache of settings.LANGUAGES in an OrderedDict for easy lookups by key.
+    Cache of settings.LANGUAGES in a dictionary for easy lookups by key.
     """
-    return OrderedDict(settings.LANGUAGES)
+    return dict(settings.LANGUAGES)
 
 
 @functools.lru_cache(maxsize=1000)

+ 1 - 2
django/utils/xmlutils.py

@@ -3,7 +3,6 @@ Utilities for XML generation/parsing.
 """
 
 import re
-from collections import OrderedDict
 from xml.sax.saxutils import XMLGenerator
 
 
@@ -30,5 +29,5 @@ class SimplerXMLGenerator(XMLGenerator):
 
     def startElement(self, name, attrs):
         # Sort attrs for a deterministic output.
-        sorted_attrs = OrderedDict(sorted(attrs.items())) if attrs else attrs
+        sorted_attrs = dict(sorted(attrs.items())) if attrs else attrs
         super().startElement(name, sorted_attrs)

+ 4 - 7
docs/ref/models/querysets.txt

@@ -1363,17 +1363,14 @@ of the arguments is required, but you should use at least one of them.
 
   In some rare cases, you might wish to pass parameters to the SQL
   fragments in ``extra(select=...)``. For this purpose, use the
-  ``select_params`` parameter. Since ``select_params`` is a sequence and
-  the ``select`` attribute is a dictionary, some care is required so that
-  the parameters are matched up correctly with the extra select pieces.
-  In this situation, you should use a :class:`collections.OrderedDict` for
-  the ``select`` value, not just a normal Python dictionary.
+  ``select_params`` parameter.
 
   This will work, for example::
 
       Blog.objects.extra(
-          select=OrderedDict([('a', '%s'), ('b', '%s')]),
-          select_params=('one', 'two'))
+          select={'a': '%s', 'b': '%s'},
+          select_params=('one', 'two'),
+      )
 
   If you need to use a literal ``%s`` inside your select string, use
   the sequence ``%%s``.

+ 18 - 22
tests/extra_regress/tests.py

@@ -1,5 +1,4 @@
 import datetime
-from collections import OrderedDict
 
 from django.contrib.auth.models import User
 from django.test import TestCase
@@ -73,10 +72,7 @@ class ExtraRegressTests(TestCase):
         # Extra select parameters should stay tied to their corresponding
         # select portions. Applies when portions are updated or otherwise
         # moved around.
-        qs = User.objects.extra(
-            select=OrderedDict((("alpha", "%s"), ("beta", "2"), ("gamma", "%s"))),
-            select_params=(1, 3)
-        )
+        qs = User.objects.extra(select={'alpha': '%s', 'beta': "2", 'gamma': '%s'}, select_params=(1, 3))
         qs = qs.extra(select={"beta": 4})
         qs = qs.extra(select={"alpha": "%s"}, select_params=[5])
         self.assertEqual(
@@ -184,7 +180,7 @@ class ExtraRegressTests(TestCase):
         self.assertEqual(
             list(
                 TestObject.objects
-                .extra(select=OrderedDict((('foo', 'first'), ('bar', 'second'), ('whiz', 'third'))))
+                .extra(select={'foo': 'first', 'bar': 'second', 'whiz': 'third'})
                 .values()
             ),
             [{
@@ -198,7 +194,7 @@ class ExtraRegressTests(TestCase):
             list(
                 TestObject.objects
                 .values()
-                .extra(select=OrderedDict((('foo', 'first'), ('bar', 'second'), ('whiz', 'third'))))
+                .extra(select={'foo': 'first', 'bar': 'second', 'whiz': 'third'})
             ),
             [{
                 'bar': 'second', 'third': 'third', 'second': 'second', 'whiz': 'third', 'foo': 'first',
@@ -210,7 +206,7 @@ class ExtraRegressTests(TestCase):
         self.assertEqual(
             list(
                 TestObject.objects
-                .extra(select=OrderedDict((('foo', 'first'), ('bar', 'second'), ('whiz', 'third'))))
+                .extra(select={'foo': 'first', 'bar': 'second', 'whiz': 'third'})
                 .values('first', 'second')
             ),
             [{'second': 'second', 'first': 'first'}]
@@ -221,7 +217,7 @@ class ExtraRegressTests(TestCase):
             list(
                 TestObject.objects
                 .values('first', 'second')
-                .extra(select=OrderedDict((('foo', 'first'), ('bar', 'second'), ('whiz', 'third'))))
+                .extra(select={'foo': 'first', 'bar': 'second', 'whiz': 'third'})
             ),
             [{'second': 'second', 'first': 'first'}]
         )
@@ -230,7 +226,7 @@ class ExtraRegressTests(TestCase):
         self.assertEqual(
             list(
                 TestObject.objects
-                .extra(select=OrderedDict((('foo', 'first'), ('bar', 'second'), ('whiz', 'third'))))
+                .extra(select={'foo': 'first', 'bar': 'second', 'whiz': 'third'})
                 .values('first', 'second', 'foo')
             ),
             [{'second': 'second', 'foo': 'first', 'first': 'first'}]
@@ -240,7 +236,7 @@ class ExtraRegressTests(TestCase):
         self.assertEqual(
             list(
                 TestObject.objects
-                .extra(select=OrderedDict((('foo', 'first'), ('bar', 'second'), ('whiz', 'third'))))
+                .extra(select={'foo': 'first', 'bar': 'second', 'whiz': 'third'})
                 .values('foo', 'whiz')
             ),
             [{'foo': 'first', 'whiz': 'third'}]
@@ -251,7 +247,7 @@ class ExtraRegressTests(TestCase):
         self.assertEqual(
             list(
                 TestObject.objects
-                .extra(select=OrderedDict((('foo', 'first'), ('bar', 'second'), ('whiz', 'third'))))
+                .extra(select={'foo': 'first', 'bar': 'second', 'whiz': 'third'})
                 .values_list()
             ),
             [('first', 'second', 'third', obj.pk, 'first', 'second', 'third')]
@@ -262,7 +258,7 @@ class ExtraRegressTests(TestCase):
             list(
                 TestObject.objects
                 .values_list()
-                .extra(select=OrderedDict((('foo', 'first'), ('bar', 'second'), ('whiz', 'third'))))
+                .extra(select={'foo': 'first', 'bar': 'second', 'whiz': 'third'})
             ),
             [('first', 'second', 'third', obj.pk, 'first', 'second', 'third')]
         )
@@ -271,7 +267,7 @@ class ExtraRegressTests(TestCase):
         self.assertEqual(
             list(
                 TestObject.objects
-                .extra(select=OrderedDict((('foo', 'first'), ('bar', 'second'), ('whiz', 'third'))))
+                .extra(select={'foo': 'first', 'bar': 'second', 'whiz': 'third'})
                 .values_list('first', 'second')
             ),
             [('first', 'second')]
@@ -282,7 +278,7 @@ class ExtraRegressTests(TestCase):
             list(
                 TestObject.objects
                 .values_list('first', 'second')
-                .extra(select=OrderedDict((('foo', 'first'), ('bar', 'second'), ('whiz', 'third'))))
+                .extra(select={'foo': 'first', 'bar': 'second', 'whiz': 'third'})
             ),
             [('first', 'second')]
         )
@@ -290,7 +286,7 @@ class ExtraRegressTests(TestCase):
         self.assertEqual(
             list(
                 TestObject.objects
-                .extra(select=OrderedDict((('foo', 'first'), ('bar', 'second'), ('whiz', 'third'))))
+                .extra(select={'foo': 'first', 'bar': 'second', 'whiz': 'third'})
                 .values_list('second', flat=True)
             ),
             ['second']
@@ -300,7 +296,7 @@ class ExtraRegressTests(TestCase):
         self.assertEqual(
             list(
                 TestObject.objects
-                .extra(select=OrderedDict((('foo', 'first'), ('bar', 'second'), ('whiz', 'third'))))
+                .extra(select={'foo': 'first', 'bar': 'second', 'whiz': 'third'})
                 .values_list('first', 'second', 'whiz')
             ),
             [('first', 'second', 'third')]
@@ -310,7 +306,7 @@ class ExtraRegressTests(TestCase):
         self.assertEqual(
             list(
                 TestObject.objects
-                .extra(select=OrderedDict((('foo', 'first'), ('bar', 'second'), ('whiz', 'third'))))
+                .extra(select={'foo': 'first', 'bar': 'second', 'whiz': 'third'})
                 .values_list('foo', 'whiz')
             ),
             [('first', 'third')]
@@ -319,7 +315,7 @@ class ExtraRegressTests(TestCase):
         self.assertEqual(
             list(
                 TestObject.objects
-                .extra(select=OrderedDict((('foo', 'first'), ('bar', 'second'), ('whiz', 'third'))))
+                .extra(select={'foo': 'first', 'bar': 'second', 'whiz': 'third'})
                 .values_list('whiz', flat=True)
             ),
             ['third']
@@ -329,7 +325,7 @@ class ExtraRegressTests(TestCase):
         self.assertEqual(
             list(
                 TestObject.objects
-                .extra(select=OrderedDict((('foo', 'first'), ('bar', 'second'), ('whiz', 'third'))))
+                .extra(select={'foo': 'first', 'bar': 'second', 'whiz': 'third'})
                 .values_list('whiz', 'foo')
             ),
             [('third', 'first')]
@@ -338,7 +334,7 @@ class ExtraRegressTests(TestCase):
         self.assertEqual(
             list(
                 TestObject.objects
-                .extra(select=OrderedDict((('foo', 'first'), ('bar', 'second'), ('whiz', 'third'))))
+                .extra(select={'foo': 'first', 'bar': 'second', 'whiz': 'third'})
                 .values_list('first', 'id')
             ),
             [('first', obj.pk)]
@@ -347,7 +343,7 @@ class ExtraRegressTests(TestCase):
         self.assertEqual(
             list(
                 TestObject.objects
-                .extra(select=OrderedDict((('foo', 'first'), ('bar', 'second'), ('whiz', 'third'))))
+                .extra(select={'foo': 'first', 'bar': 'second', 'whiz': 'third'})
                 .values_list('whiz', 'first', 'bar', 'id')
             ),
             [('third', 'first', 'second', obj.pk)]

+ 2 - 32
tests/queries/tests.py

@@ -1,7 +1,6 @@
 import datetime
 import pickle
 import unittest
-from collections import OrderedDict
 from operator import attrgetter
 
 from django.core.exceptions import EmptyResultSet, FieldError
@@ -543,31 +542,6 @@ class Queries1Tests(TestCase):
         # ...or use the field name.
         self.assertSequenceEqual(ExtraInfo.objects.values('note'), [{'note': 1}, {'note': 2}])
 
-    def test_ticket2902(self):
-        # Parameters can be given to extra_select, *if* you use an OrderedDict.
-
-        # (First we need to know which order the keys fall in "naturally" on
-        # your system, so we can put things in the wrong way around from
-        # normal. A normal dict would thus fail.)
-        s = [('a', '%s'), ('b', '%s')]
-        params = ['one', 'two']
-        if list({'a': 1, 'b': 2}) == ['a', 'b']:
-            s.reverse()
-            params.reverse()
-
-        d = Item.objects.extra(select=OrderedDict(s), select_params=params).values('a', 'b')[0]
-        self.assertEqual(d, {'a': 'one', 'b': 'two'})
-
-        # Order by the number of tags attached to an item.
-        qs = (
-            Item.objects
-            .extra(select={
-                'count': 'select count(*) from queries_item_tags where queries_item_tags.item_id = queries_item.id'
-            })
-            .order_by('-count')
-        )
-        self.assertEqual([o.count for o in qs], [2, 2, 1, 0])
-
     def test_ticket6154(self):
         # Multiple filter statements are joined using "AND" all the time.
 
@@ -2249,9 +2223,7 @@ class ValuesQuerysetTests(TestCase):
 
     def test_extra_values(self):
         # testing for ticket 14930 issues
-        qs = Number.objects.extra(select=OrderedDict([('value_plus_x', 'num+%s'),
-                                                     ('value_minus_x', 'num-%s')]),
-                                  select_params=(1, 2))
+        qs = Number.objects.extra(select={'value_plus_x': 'num+%s', 'value_minus_x': 'num-%s'}, select_params=(1, 2))
         qs = qs.order_by('value_minus_x')
         qs = qs.values('num')
         self.assertSequenceEqual(qs, [{'num': 72}])
@@ -2295,9 +2267,7 @@ class ValuesQuerysetTests(TestCase):
 
     def test_extra_multiple_select_params_values_order_by(self):
         # testing for 23259 issue
-        qs = Number.objects.extra(select=OrderedDict([('value_plus_x', 'num+%s'),
-                                                     ('value_minus_x', 'num-%s')]),
-                                  select_params=(72, 72))
+        qs = Number.objects.extra(select={'value_plus_x': 'num+%s', 'value_minus_x': 'num-%s'}, select_params=(72, 72))
         qs = qs.order_by('value_minus_x')
         qs = qs.filter(num=1)
         qs = qs.values('num')

+ 21 - 22
tests/sitemaps_tests/urls/http.py

@@ -1,4 +1,3 @@
-from collections import OrderedDict
 from datetime import date, datetime
 
 from django.conf.urls.i18n import i18n_patterns
@@ -102,27 +101,27 @@ fixed_lastmod__mixed_sitemaps = {
     'fixed-lastmod-mixed': FixedLastmodMixedSitemap,
 }
 
-sitemaps_lastmod_mixed_ascending = OrderedDict([
-    ('no-lastmod', EmptySitemap),
-    ('lastmod', FixedLastmodSitemap),
-])
-
-sitemaps_lastmod_mixed_descending = OrderedDict([
-    ('lastmod', FixedLastmodSitemap),
-    ('no-lastmod', EmptySitemap),
-])
-
-sitemaps_lastmod_ascending = OrderedDict([
-    ('date', DateSiteMap),
-    ('datetime', FixedLastmodSitemap),
-    ('datetime-newer', FixedNewerLastmodSitemap),
-])
-
-sitemaps_lastmod_descending = OrderedDict([
-    ('datetime-newer', FixedNewerLastmodSitemap),
-    ('datetime', FixedLastmodSitemap),
-    ('date', DateSiteMap),
-])
+sitemaps_lastmod_mixed_ascending = {
+    'no-lastmod': EmptySitemap,
+    'lastmod': FixedLastmodSitemap,
+}
+
+sitemaps_lastmod_mixed_descending = {
+    'lastmod': FixedLastmodSitemap,
+    'no-lastmod': EmptySitemap,
+}
+
+sitemaps_lastmod_ascending = {
+    'date': DateSiteMap,
+    'datetime': FixedLastmodSitemap,
+    'datetime-newer': FixedNewerLastmodSitemap,
+}
+
+sitemaps_lastmod_descending = {
+    'datetime-newer': FixedNewerLastmodSitemap,
+    'datetime': FixedLastmodSitemap,
+    'date': DateSiteMap,
+}
 
 generic_sitemaps = {
     'generic': GenericSitemap({'queryset': TestModel.objects.order_by('pk').all()}),