diff --git a/django/apps/registry.py b/django/apps/registry.py index 234a830fb9..408964a146 100644 --- a/django/apps/registry.py +++ b/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) diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py index ab4aa6d93b..d170f2f291 100644 --- a/django/contrib/admin/options.py +++ b/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): """ diff --git a/django/contrib/admin/views/main.py b/django/contrib/admin/views/main.py index 298e18c57e..978910df26 100644 --- a/django/contrib/admin/views/main.py +++ b/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 diff --git a/django/contrib/auth/hashers.py b/django/contrib/auth/hashers.py index 04025ec7e4..34caa4eba9 100644 --- a/django/contrib/auth/hashers.py +++ b/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 diff --git a/django/contrib/staticfiles/finders.py b/django/contrib/staticfiles/finders.py index 8d8f1cb983..53d31b9b1f 100644 --- a/django/contrib/staticfiles/finders.py +++ b/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) diff --git a/django/contrib/staticfiles/management/commands/collectstatic.py b/django/contrib/staticfiles/management/commands/collectstatic.py index cccaf1b9c2..238a30aa98 100644 --- a/django/contrib/staticfiles/management/commands/collectstatic.py +++ b/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 diff --git a/django/contrib/staticfiles/storage.py b/django/contrib/staticfiles/storage.py index e49febe611..130d6270bb 100644 --- a/django/contrib/staticfiles/storage.py +++ b/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() diff --git a/django/core/management/__init__.py b/django/core/management/__init__.py index e0c924bdac..b8f47fa4a4 100644 --- a/django/core/management/__init__.py +++ b/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 diff --git a/django/core/management/commands/dumpdata.py b/django/core/management/commands/dumpdata.py index 31df5ac244..ce936c5459 100644 --- a/django/core/management/commands/dumpdata.py +++ b/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('.') diff --git a/django/core/management/commands/inspectdb.py b/django/core/management/commands/inspectdb.py index 92f010a064..92c2035877 100644 --- a/django/core/management/commands/inspectdb.py +++ b/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: diff --git a/django/core/management/commands/migrate.py b/django/core/management/commands/migrate.py index c2d0c16816..551804a72e 100644 --- a/django/core/management/commands/migrate.py +++ b/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: diff --git a/django/core/serializers/python.py b/django/core/serializers/python.py index 08739c98fc..5a5d8a7036 100644 --- a/django/core/serializers/python.py +++ b/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 diff --git a/django/core/serializers/pyyaml.py b/django/core/serializers/pyyaml.py index ed3d391d51..778c933584 100644 --- a/django/core/serializers/pyyaml.py +++ b/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): diff --git a/django/db/migrations/serializer.py b/django/db/migrations/serializer.py index ace0a860c4..239b17e974 100644 --- a/django/db/migrations/serializer.py +++ b/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): diff --git a/django/db/migrations/state.py b/django/db/migrations/state.py index a20aa0bd4a..9b62edad1f 100644 --- a/django/db/migrations/state.py +++ b/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() diff --git a/django/db/models/deletion.py b/django/db/models/deletion.py index 0a1c0338c1..92fe4affb9 100644 --- a/django/db/models/deletion.py +++ b/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 diff --git a/django/db/models/options.py b/django/db/models/options.py index b8b48cddac..fea65f7626 100644 --- a/django/db/models/options.py +++ b/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 diff --git a/django/db/models/query.py b/django/db/models/query.py index 5c2f352636..41225f2b1a 100644 --- a/django/db/models/query.py +++ b/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: diff --git a/django/db/models/sql/compiler.py b/django/db/models/sql/compiler.py index dd5a2e8c39..39ec6eacfc 100644 --- a/django/db/models/sql/compiler.py +++ b/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() diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py index 675ff8c176..5574941ef5 100644 --- a/django/db/models/sql/query.py +++ b/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 diff --git a/django/forms/forms.py b/django/forms/forms.py index 51e6465539..0c49a94432 100644 --- a/django/forms/forms.py +++ b/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) diff --git a/django/forms/models.py b/django/forms/models.py index fe8a67ed2b..d157c291ef 100644 --- a/django/forms/models.py +++ b/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 diff --git a/django/template/utils.py b/django/template/utils.py index 5a6c414939..f4ed2750c2 100644 --- a/django/template/utils.py +++ b/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: diff --git a/django/test/utils.py b/django/test/utils.py index 419180eed3..8bdcea77d7 100644 --- a/django/test/utils.py +++ b/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 diff --git a/django/utils/datastructures.py b/django/utils/datastructures.py index 191c2348f6..083cf3cb8b 100644 --- a/django/utils/datastructures.py +++ b/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 diff --git a/django/utils/translation/trans_real.py b/django/utils/translation/trans_real.py index 0e53b223e5..d6f1f7f14f 100644 --- a/django/utils/translation/trans_real.py +++ b/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) diff --git a/django/utils/xmlutils.py b/django/utils/xmlutils.py index 1a14034243..e4607b9865 100644 --- a/django/utils/xmlutils.py +++ b/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) diff --git a/docs/ref/models/querysets.txt b/docs/ref/models/querysets.txt index d09618985f..2bd347701b 100644 --- a/docs/ref/models/querysets.txt +++ b/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``. diff --git a/tests/extra_regress/tests.py b/tests/extra_regress/tests.py index 67625235f4..01bedcbbeb 100644 --- a/tests/extra_regress/tests.py +++ b/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)] diff --git a/tests/queries/tests.py b/tests/queries/tests.py index c655fe52cb..60e4243315 100644 --- a/tests/queries/tests.py +++ b/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') diff --git a/tests/sitemaps_tests/urls/http.py b/tests/sitemaps_tests/urls/http.py index 03652902fb..495f60fb1a 100644 --- a/tests/sitemaps_tests/urls/http.py +++ b/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_ascending = { + 'no-lastmod': EmptySitemap, + 'lastmod': FixedLastmodSitemap, +} -sitemaps_lastmod_mixed_descending = OrderedDict([ - ('lastmod', FixedLastmodSitemap), - ('no-lastmod', EmptySitemap), -]) +sitemaps_lastmod_mixed_descending = { + 'lastmod': FixedLastmodSitemap, + 'no-lastmod': EmptySitemap, +} -sitemaps_lastmod_ascending = OrderedDict([ - ('date', DateSiteMap), - ('datetime', FixedLastmodSitemap), - ('datetime-newer', FixedNewerLastmodSitemap), -]) +sitemaps_lastmod_ascending = { + 'date': DateSiteMap, + 'datetime': FixedLastmodSitemap, + 'datetime-newer': FixedNewerLastmodSitemap, +} -sitemaps_lastmod_descending = OrderedDict([ - ('datetime-newer', FixedNewerLastmodSitemap), - ('datetime', FixedLastmodSitemap), - ('date', DateSiteMap), -]) +sitemaps_lastmod_descending = { + 'datetime-newer': FixedNewerLastmodSitemap, + 'datetime': FixedLastmodSitemap, + 'date': DateSiteMap, +} generic_sitemaps = { 'generic': GenericSitemap({'queryset': TestModel.objects.order_by('pk').all()}),