Deprecated SortedDict (replaced with collections.OrderedDict)

Thanks Loic Bistuer for the review.
This commit is contained in:
Curtis Maloney 2013-08-03 15:41:15 +10:00 committed by Tim Graham
parent b278f7478d
commit 07876cf02b
26 changed files with 139 additions and 107 deletions

View File

@ -1,3 +1,4 @@
from collections import OrderedDict
import copy import copy
import operator import operator
from functools import partial, reduce, update_wrapper from functools import partial, reduce, update_wrapper
@ -29,7 +30,6 @@ from django.http.response import HttpResponseBase
from django.shortcuts import get_object_or_404 from django.shortcuts import get_object_or_404
from django.template.response import SimpleTemplateResponse, TemplateResponse from django.template.response import SimpleTemplateResponse, TemplateResponse
from django.utils.decorators import method_decorator from django.utils.decorators import method_decorator
from django.utils.datastructures import SortedDict
from django.utils.html import escape, escapejs from django.utils.html import escape, escapejs
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.utils import six from django.utils import six
@ -672,7 +672,7 @@ class ModelAdmin(BaseModelAdmin):
# want *any* actions enabled on this page. # want *any* actions enabled on this page.
from django.contrib.admin.views.main import _is_changelist_popup from django.contrib.admin.views.main import _is_changelist_popup
if self.actions is None or _is_changelist_popup(request): if self.actions is None or _is_changelist_popup(request):
return SortedDict() return OrderedDict()
actions = [] actions = []
@ -693,8 +693,8 @@ class ModelAdmin(BaseModelAdmin):
# get_action might have returned None, so filter any of those out. # get_action might have returned None, so filter any of those out.
actions = filter(None, actions) actions = filter(None, actions)
# Convert the actions into a SortedDict keyed by name. # Convert the actions into an OrderedDict keyed by name.
actions = SortedDict([ actions = OrderedDict([
(name, (func, name, desc)) (name, (func, name, desc))
for func, name, desc in actions for func, name, desc in actions
]) ])

View File

@ -1,3 +1,4 @@
from collections import OrderedDict
import sys import sys
import warnings import warnings
@ -7,7 +8,6 @@ from django.core.urlresolvers import reverse
from django.db import models from django.db import models
from django.db.models.fields import FieldDoesNotExist from django.db.models.fields import FieldDoesNotExist
from django.utils import six from django.utils import six
from django.utils.datastructures import SortedDict
from django.utils.deprecation import RenameMethodsBase from django.utils.deprecation import RenameMethodsBase
from django.utils.encoding import force_str, force_text from django.utils.encoding import force_str, force_text
from django.utils.translation import ugettext, ugettext_lazy from django.utils.translation import ugettext, ugettext_lazy
@ -319,13 +319,13 @@ class ChangeList(six.with_metaclass(RenameChangeListMethods)):
def get_ordering_field_columns(self): def get_ordering_field_columns(self):
""" """
Returns a SortedDict of ordering field column numbers and asc/desc Returns an OrderedDict of ordering field column numbers and asc/desc
""" """
# We must cope with more than one column having the same underlying sort # We must cope with more than one column having the same underlying sort
# field, so we base things on column numbers. # field, so we base things on column numbers.
ordering = self._get_default_ordering() ordering = self._get_default_ordering()
ordering_fields = SortedDict() ordering_fields = OrderedDict()
if ORDER_VAR not in self.params: if ORDER_VAR not in self.params:
# for ordering specified on ModelAdmin or model Meta, we don't know # for ordering specified on ModelAdmin or model Meta, we don't know
# the right column numbers absolutely, because there might be more # the right column numbers absolutely, because there might be more

View File

@ -1,9 +1,10 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from collections import OrderedDict
from django import forms from django import forms
from django.forms.util import flatatt from django.forms.util import flatatt
from django.template import loader from django.template import loader
from django.utils.datastructures import SortedDict
from django.utils.encoding import force_bytes from django.utils.encoding import force_bytes
from django.utils.html import format_html, format_html_join from django.utils.html import format_html, format_html_join
from django.utils.http import urlsafe_base64_encode from django.utils.http import urlsafe_base64_encode
@ -324,7 +325,7 @@ class PasswordChangeForm(SetPasswordForm):
) )
return old_password return old_password
PasswordChangeForm.base_fields = SortedDict([ PasswordChangeForm.base_fields = OrderedDict([
(k, PasswordChangeForm.base_fields[k]) (k, PasswordChangeForm.base_fields[k])
for k in ['old_password', 'new_password1', 'new_password2'] for k in ['old_password', 'new_password1', 'new_password2']
]) ])

View File

@ -2,13 +2,13 @@ from __future__ import unicode_literals
import base64 import base64
import binascii import binascii
from collections import OrderedDict
import hashlib import hashlib
import importlib import importlib
from django.dispatch import receiver from django.dispatch import receiver
from django.conf import settings from django.conf import settings
from django.test.signals import setting_changed from django.test.signals import setting_changed
from django.utils.datastructures import SortedDict
from django.utils.encoding import force_bytes, force_str, force_text from django.utils.encoding import force_bytes, force_str, force_text
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
from django.utils.crypto import ( from django.utils.crypto import (
@ -243,7 +243,7 @@ class PBKDF2PasswordHasher(BasePasswordHasher):
def safe_summary(self, encoded): def safe_summary(self, encoded):
algorithm, iterations, salt, hash = encoded.split('$', 3) algorithm, iterations, salt, hash = encoded.split('$', 3)
assert algorithm == self.algorithm assert algorithm == self.algorithm
return SortedDict([ return OrderedDict([
(_('algorithm'), algorithm), (_('algorithm'), algorithm),
(_('iterations'), iterations), (_('iterations'), iterations),
(_('salt'), mask_hash(salt)), (_('salt'), mask_hash(salt)),
@ -320,7 +320,7 @@ class BCryptSHA256PasswordHasher(BasePasswordHasher):
algorithm, empty, algostr, work_factor, data = encoded.split('$', 4) algorithm, empty, algostr, work_factor, data = encoded.split('$', 4)
assert algorithm == self.algorithm assert algorithm == self.algorithm
salt, checksum = data[:22], data[22:] salt, checksum = data[:22], data[22:]
return SortedDict([ return OrderedDict([
(_('algorithm'), algorithm), (_('algorithm'), algorithm),
(_('work factor'), work_factor), (_('work factor'), work_factor),
(_('salt'), mask_hash(salt)), (_('salt'), mask_hash(salt)),
@ -368,7 +368,7 @@ class SHA1PasswordHasher(BasePasswordHasher):
def safe_summary(self, encoded): def safe_summary(self, encoded):
algorithm, salt, hash = encoded.split('$', 2) algorithm, salt, hash = encoded.split('$', 2)
assert algorithm == self.algorithm assert algorithm == self.algorithm
return SortedDict([ return OrderedDict([
(_('algorithm'), algorithm), (_('algorithm'), algorithm),
(_('salt'), mask_hash(salt, show=2)), (_('salt'), mask_hash(salt, show=2)),
(_('hash'), mask_hash(hash)), (_('hash'), mask_hash(hash)),
@ -396,7 +396,7 @@ class MD5PasswordHasher(BasePasswordHasher):
def safe_summary(self, encoded): def safe_summary(self, encoded):
algorithm, salt, hash = encoded.split('$', 2) algorithm, salt, hash = encoded.split('$', 2)
assert algorithm == self.algorithm assert algorithm == self.algorithm
return SortedDict([ return OrderedDict([
(_('algorithm'), algorithm), (_('algorithm'), algorithm),
(_('salt'), mask_hash(salt, show=2)), (_('salt'), mask_hash(salt, show=2)),
(_('hash'), mask_hash(hash)), (_('hash'), mask_hash(hash)),
@ -429,7 +429,7 @@ class UnsaltedSHA1PasswordHasher(BasePasswordHasher):
def safe_summary(self, encoded): def safe_summary(self, encoded):
assert encoded.startswith('sha1$$') assert encoded.startswith('sha1$$')
hash = encoded[6:] hash = encoded[6:]
return SortedDict([ return OrderedDict([
(_('algorithm'), self.algorithm), (_('algorithm'), self.algorithm),
(_('hash'), mask_hash(hash)), (_('hash'), mask_hash(hash)),
]) ])
@ -462,7 +462,7 @@ class UnsaltedMD5PasswordHasher(BasePasswordHasher):
return constant_time_compare(encoded, encoded_2) return constant_time_compare(encoded, encoded_2)
def safe_summary(self, encoded): def safe_summary(self, encoded):
return SortedDict([ return OrderedDict([
(_('algorithm'), self.algorithm), (_('algorithm'), self.algorithm),
(_('hash'), mask_hash(encoded, show=3)), (_('hash'), mask_hash(encoded, show=3)),
]) ])
@ -496,7 +496,7 @@ class CryptPasswordHasher(BasePasswordHasher):
def safe_summary(self, encoded): def safe_summary(self, encoded):
algorithm, salt, data = encoded.split('$', 2) algorithm, salt, data = encoded.split('$', 2)
assert algorithm == self.algorithm assert algorithm == self.algorithm
return SortedDict([ return OrderedDict([
(_('algorithm'), algorithm), (_('algorithm'), algorithm),
(_('salt'), salt), (_('salt'), salt),
(_('hash'), mask_hash(data, show=3)), (_('hash'), mask_hash(data, show=3)),

View File

@ -1,3 +1,4 @@
from collections import OrderedDict
import re import re
from django import forms from django import forms
@ -5,7 +6,6 @@ from django.shortcuts import redirect
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.forms import formsets, ValidationError from django.forms import formsets, ValidationError
from django.views.generic import TemplateView from django.views.generic import TemplateView
from django.utils.datastructures import SortedDict
from django.utils.decorators import classonlymethod from django.utils.decorators import classonlymethod
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.utils import six from django.utils import six
@ -158,7 +158,7 @@ class WizardView(TemplateView):
form_list = form_list or kwargs.pop('form_list', form_list = form_list or kwargs.pop('form_list',
getattr(cls, 'form_list', None)) or [] getattr(cls, 'form_list', None)) or []
computed_form_list = SortedDict() computed_form_list = OrderedDict()
assert len(form_list) > 0, 'at least one form is needed' assert len(form_list) > 0, 'at least one form is needed'
@ -206,7 +206,7 @@ class WizardView(TemplateView):
The form_list is always generated on the fly because condition methods The form_list is always generated on the fly because condition methods
could use data from other (maybe previous forms). could use data from other (maybe previous forms).
""" """
form_list = SortedDict() form_list = OrderedDict()
for form_key, form_class in six.iteritems(self.form_list): for form_key, form_class in six.iteritems(self.form_list):
# try to fetch the value from condition list, by default, the form # try to fetch the value from condition list, by default, the form
# gets passed to the new list. # gets passed to the new list.
@ -498,9 +498,10 @@ class WizardView(TemplateView):
if step is None: if step is None:
step = self.steps.current step = self.steps.current
form_list = self.get_form_list() form_list = self.get_form_list()
key = form_list.keyOrder.index(step) + 1 keys = list(form_list.keys())
if len(form_list.keyOrder) > key: key = keys.index(step) + 1
return form_list.keyOrder[key] if len(keys) > key:
return keys[key]
return None return None
def get_prev_step(self, step=None): def get_prev_step(self, step=None):
@ -512,9 +513,10 @@ class WizardView(TemplateView):
if step is None: if step is None:
step = self.steps.current step = self.steps.current
form_list = self.get_form_list() form_list = self.get_form_list()
key = form_list.keyOrder.index(step) - 1 keys = list(form_list.keys())
key = keys.index(step) - 1
if key >= 0: if key >= 0:
return form_list.keyOrder[key] return keys[key]
return None return None
def get_step_index(self, step=None): def get_step_index(self, step=None):
@ -524,7 +526,7 @@ class WizardView(TemplateView):
""" """
if step is None: if step is None:
step = self.steps.current step = self.steps.current
return self.get_form_list().keyOrder.index(step) return list(self.get_form_list().keys()).index(step)
def get_context_data(self, form, **kwargs): def get_context_data(self, form, **kwargs):
""" """

View File

@ -1,8 +1,9 @@
from collections import OrderedDict
import os import os
from django.conf import settings from django.conf import settings
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
from django.core.files.storage import default_storage, Storage, FileSystemStorage from django.core.files.storage import default_storage, Storage, FileSystemStorage
from django.utils.datastructures import SortedDict
from django.utils.functional import empty, memoize, LazyObject from django.utils.functional import empty, memoize, LazyObject
from django.utils.module_loading import import_by_path from django.utils.module_loading import import_by_path
from django.utils._os import safe_join from django.utils._os import safe_join
@ -11,7 +12,7 @@ from django.utils import six
from django.contrib.staticfiles import utils from django.contrib.staticfiles import utils
from django.contrib.staticfiles.storage import AppStaticStorage from django.contrib.staticfiles.storage import AppStaticStorage
_finders = SortedDict() _finders = OrderedDict()
class BaseFinder(object): class BaseFinder(object):
@ -47,7 +48,7 @@ class FileSystemFinder(BaseFinder):
# List of locations with static files # List of locations with static files
self.locations = [] self.locations = []
# Maps dir paths to an appropriate storage instance # Maps dir paths to an appropriate storage instance
self.storages = SortedDict() self.storages = OrderedDict()
if not isinstance(settings.STATICFILES_DIRS, (list, tuple)): if not isinstance(settings.STATICFILES_DIRS, (list, tuple)):
raise ImproperlyConfigured( raise ImproperlyConfigured(
"Your STATICFILES_DIRS setting is not a tuple or list; " "Your STATICFILES_DIRS setting is not a tuple or list; "
@ -118,7 +119,7 @@ class AppDirectoriesFinder(BaseFinder):
# The list of apps that are handled # The list of apps that are handled
self.apps = [] self.apps = []
# Mapping of app module paths to storage instances # Mapping of app module paths to storage instances
self.storages = SortedDict() self.storages = OrderedDict()
if apps is None: if apps is None:
apps = settings.INSTALLED_APPS apps = settings.INSTALLED_APPS
for app in apps: for app in apps:

View File

@ -2,12 +2,12 @@ from __future__ import unicode_literals
import os import os
import sys import sys
from collections import OrderedDict
from optparse import make_option from optparse import make_option
from django.core.files.storage import FileSystemStorage from django.core.files.storage import FileSystemStorage
from django.core.management.base import CommandError, NoArgsCommand from django.core.management.base import CommandError, NoArgsCommand
from django.utils.encoding import smart_text from django.utils.encoding import smart_text
from django.utils.datastructures import SortedDict
from django.utils.six.moves import input from django.utils.six.moves import input
from django.contrib.staticfiles import finders, storage from django.contrib.staticfiles import finders, storage
@ -97,7 +97,7 @@ class Command(NoArgsCommand):
else: else:
handler = self.copy_file handler = self.copy_file
found_files = SortedDict() found_files = OrderedDict()
for finder in finders.get_finders(): for finder in finders.get_finders():
for path, storage in finder.list(self.ignore_patterns): for path, storage in finder.list(self.ignore_patterns):
# Prefix the relative path if the source storage contains it # Prefix the relative path if the source storage contains it

View File

@ -1,4 +1,5 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from collections import OrderedDict
import hashlib import hashlib
from importlib import import_module from importlib import import_module
import os import os
@ -16,7 +17,6 @@ from django.core.cache import (get_cache, InvalidCacheBackendError,
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
from django.core.files.base import ContentFile from django.core.files.base import ContentFile
from django.core.files.storage import FileSystemStorage, get_storage_class from django.core.files.storage import FileSystemStorage, get_storage_class
from django.utils.datastructures import SortedDict
from django.utils.encoding import force_bytes, force_text from django.utils.encoding import force_bytes, force_text
from django.utils.functional import LazyObject from django.utils.functional import LazyObject
from django.utils._os import upath from django.utils._os import upath
@ -64,7 +64,7 @@ class CachedFilesMixin(object):
except InvalidCacheBackendError: except InvalidCacheBackendError:
# Use the default backend # Use the default backend
self.cache = default_cache self.cache = default_cache
self._patterns = SortedDict() self._patterns = OrderedDict()
for extension, patterns in self.patterns: for extension, patterns in self.patterns:
for pattern in patterns: for pattern in patterns:
if isinstance(pattern, (tuple, list)): if isinstance(pattern, (tuple, list)):
@ -202,7 +202,7 @@ class CachedFilesMixin(object):
def post_process(self, paths, dry_run=False, **options): def post_process(self, paths, dry_run=False, **options):
""" """
Post process the given SortedDict of files (called from collectstatic). Post process the given OrderedDict of files (called from collectstatic).
Processing is actually two separate operations: Processing is actually two separate operations:

View File

@ -1,10 +1,11 @@
from collections import OrderedDict
from optparse import make_option
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
from django.core.management.base import BaseCommand, CommandError from django.core.management.base import BaseCommand, CommandError
from django.core import serializers from django.core import serializers
from django.db import router, DEFAULT_DB_ALIAS from django.db import router, DEFAULT_DB_ALIAS
from django.utils.datastructures import SortedDict
from optparse import make_option
class Command(BaseCommand): class Command(BaseCommand):
option_list = BaseCommand.option_list + ( option_list = BaseCommand.option_list + (
@ -66,11 +67,11 @@ class Command(BaseCommand):
if len(app_labels) == 0: if len(app_labels) == 0:
if primary_keys: if primary_keys:
raise CommandError("You can only use --pks option with one model") raise CommandError("You can only use --pks option with one model")
app_list = SortedDict((app, None) for app in get_apps() if app not in excluded_apps) app_list = OrderedDict((app, None) for app in get_apps() if app not in excluded_apps)
else: else:
if len(app_labels) > 1 and primary_keys: if len(app_labels) > 1 and primary_keys:
raise CommandError("You can only use --pks option with one model") raise CommandError("You can only use --pks option with one model")
app_list = SortedDict() app_list = OrderedDict()
for label in app_labels: for label in app_labels:
try: try:
app_label, model_label = label.split('.') app_label, model_label = label.split('.')

View File

@ -1,12 +1,12 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from collections import OrderedDict
import keyword import keyword
import re import re
from optparse import make_option from optparse import make_option
from django.core.management.base import NoArgsCommand, CommandError from django.core.management.base import NoArgsCommand, CommandError
from django.db import connections, DEFAULT_DB_ALIAS from django.db import connections, DEFAULT_DB_ALIAS
from django.utils.datastructures import SortedDict
class Command(NoArgsCommand): class Command(NoArgsCommand):
@ -69,7 +69,7 @@ class Command(NoArgsCommand):
used_column_names = [] # Holds column names used in the table so far used_column_names = [] # Holds column names used in the table so far
for i, row in enumerate(connection.introspection.get_table_description(cursor, table_name)): for i, row in enumerate(connection.introspection.get_table_description(cursor, table_name)):
comment_notes = [] # Holds Field notes, to be displayed in a Python comment. comment_notes = [] # Holds Field notes, to be displayed in a Python comment.
extra_params = SortedDict() # Holds Field parameters such as 'db_column'. extra_params = OrderedDict() # Holds Field parameters such as 'db_column'.
column_name = row[0] column_name = row[0]
is_relation = i in relations is_relation = i in relations
@ -193,7 +193,7 @@ class Command(NoArgsCommand):
description, this routine will return the given field type name, as description, this routine will return the given field type name, as
well as any additional keyword parameters and notes for the field. well as any additional keyword parameters and notes for the field.
""" """
field_params = SortedDict() field_params = OrderedDict()
field_notes = [] field_notes = []
try: try:

View File

@ -1,3 +1,4 @@
from collections import OrderedDict
from importlib import import_module from importlib import import_module
from optparse import make_option from optparse import make_option
import itertools import itertools
@ -9,7 +10,6 @@ from django.core.management.base import NoArgsCommand
from django.core.management.color import no_style from django.core.management.color import no_style
from django.core.management.sql import custom_sql_for_model, emit_post_sync_signal, emit_pre_sync_signal from django.core.management.sql import custom_sql_for_model, emit_post_sync_signal, emit_pre_sync_signal
from django.db import connections, router, transaction, models, DEFAULT_DB_ALIAS from django.db import connections, router, transaction, models, DEFAULT_DB_ALIAS
from django.utils.datastructures import SortedDict
class Command(NoArgsCommand): class Command(NoArgsCommand):
@ -76,7 +76,7 @@ class Command(NoArgsCommand):
return not ((converter(opts.db_table) in tables) or return not ((converter(opts.db_table) in tables) or
(opts.auto_created and converter(opts.auto_created._meta.db_table) in tables)) (opts.auto_created and converter(opts.auto_created._meta.db_table) in tables))
manifest = SortedDict( manifest = OrderedDict(
(app_name, list(filter(model_installed, model_list))) (app_name, list(filter(model_installed, model_list)))
for app_name, model_list in all_models for app_name, model_list in all_models
) )

View File

@ -1,8 +1,8 @@
from collections import OrderedDict
from operator import attrgetter from operator import attrgetter
from django.db import connections, transaction, IntegrityError from django.db import connections, transaction, IntegrityError
from django.db.models import signals, sql from django.db.models import signals, sql
from django.utils.datastructures import SortedDict
from django.utils import six from django.utils import six
@ -234,7 +234,7 @@ class Collector(object):
found = True found = True
if not found: if not found:
return return
self.data = SortedDict([(model, self.data[model]) self.data = OrderedDict([(model, self.data[model])
for model in sorted_models]) for model in sorted_models])
def delete(self): def delete(self):

View File

@ -1,5 +1,7 @@
"Utilities for loading models and the modules that contain them." "Utilities for loading models and the modules that contain them."
from collections import OrderedDict
import copy
import imp import imp
from importlib import import_module from importlib import import_module
import os import os
@ -7,7 +9,6 @@ import sys
from django.conf import settings from django.conf import settings
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
from django.utils.datastructures import SortedDict
from django.utils.module_loading import module_has_submodule from django.utils.module_loading import module_has_submodule
from django.utils._os import upath from django.utils._os import upath
from django.utils import six from django.utils import six
@ -17,6 +18,14 @@ __all__ = ('get_apps', 'get_app', 'get_models', 'get_model', 'register_models',
MODELS_MODULE_NAME = 'models' MODELS_MODULE_NAME = 'models'
class ModelDict(OrderedDict):
"""
We need to special-case the deepcopy for this, as the keys are modules,
which can't be deep copied.
"""
def __deepcopy__(self, memo):
return self.__class__([(key, copy.deepcopy(value, memo))
for key, value in self.items()])
class UnavailableApp(Exception): class UnavailableApp(Exception):
pass pass
@ -31,14 +40,14 @@ class AppCache(object):
# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/66531. # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/66531.
__shared_state = dict( __shared_state = dict(
# Keys of app_store are the model modules for each application. # Keys of app_store are the model modules for each application.
app_store=SortedDict(), app_store=ModelDict(),
# Mapping of installed app_labels to model modules for that app. # Mapping of installed app_labels to model modules for that app.
app_labels={}, app_labels={},
# Mapping of app_labels to a dictionary of model names to model code. # Mapping of app_labels to a dictionary of model names to model code.
# May contain apps that are not installed. # May contain apps that are not installed.
app_models=SortedDict(), app_models=ModelDict(),
# Mapping of app_labels to errors raised when trying to import the app. # Mapping of app_labels to errors raised when trying to import the app.
app_errors={}, app_errors={},
@ -244,12 +253,12 @@ class AppCache(object):
if app_mod: if app_mod:
if app_mod in self.app_store: if app_mod in self.app_store:
app_list = [self.app_models.get(self._label_for(app_mod), app_list = [self.app_models.get(self._label_for(app_mod),
SortedDict())] ModelDict())]
else: else:
app_list = [] app_list = []
else: else:
if only_installed: if only_installed:
app_list = [self.app_models.get(app_label, SortedDict()) app_list = [self.app_models.get(app_label, ModelDict())
for app_label in six.iterkeys(self.app_labels)] for app_label in six.iterkeys(self.app_labels)]
else: else:
app_list = six.itervalues(self.app_models) app_list = six.itervalues(self.app_models)
@ -298,7 +307,7 @@ class AppCache(object):
# Store as 'name: model' pair in a dictionary # Store as 'name: model' pair in a dictionary
# in the app_models dictionary # in the app_models dictionary
model_name = model._meta.model_name model_name = model._meta.model_name
model_dict = self.app_models.setdefault(app_label, SortedDict()) model_dict = self.app_models.setdefault(app_label, ModelDict())
if model_name in model_dict: if model_name in model_dict:
# The same model may be imported via different paths (e.g. # The same model may be imported via different paths (e.g.
# appname.models and project.appname.models). We use the source # appname.models and project.appname.models). We use the source

View File

@ -1,5 +1,6 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from collections import OrderedDict
import re import re
from bisect import bisect from bisect import bisect
import warnings import warnings
@ -11,7 +12,6 @@ from django.db.models.fields.proxy import OrderWrt
from django.db.models.loading import get_models, app_cache_ready from django.db.models.loading import get_models, app_cache_ready
from django.utils import six from django.utils import six
from django.utils.functional import cached_property from django.utils.functional import cached_property
from django.utils.datastructures import SortedDict
from django.utils.encoding import force_text, smart_text, python_2_unicode_compatible from django.utils.encoding import force_text, smart_text, python_2_unicode_compatible
from django.utils.translation import activate, deactivate_all, get_language, string_concat from django.utils.translation import activate, deactivate_all, get_language, string_concat
@ -58,7 +58,7 @@ class Options(object):
# concrete models, the concrete_model is always the class itself. # concrete models, the concrete_model is always the class itself.
self.concrete_model = None self.concrete_model = None
self.swappable = None self.swappable = None
self.parents = SortedDict() self.parents = OrderedDict()
self.auto_created = False self.auto_created = False
# To handle various inheritance situations, we need to track where # To handle various inheritance situations, we need to track where
@ -332,7 +332,7 @@ class Options(object):
return list(six.iteritems(self._m2m_cache)) return list(six.iteritems(self._m2m_cache))
def _fill_m2m_cache(self): def _fill_m2m_cache(self):
cache = SortedDict() cache = OrderedDict()
for parent in self.parents: for parent in self.parents:
for field, model in parent._meta.get_m2m_with_model(): for field, model in parent._meta.get_m2m_with_model():
if model: if model:
@ -474,7 +474,7 @@ class Options(object):
return [t for t in cache.items() if all(p(*t) for p in predicates)] return [t for t in cache.items() if all(p(*t) for p in predicates)]
def _fill_related_objects_cache(self): def _fill_related_objects_cache(self):
cache = SortedDict() cache = OrderedDict()
parent_list = self.get_parent_list() parent_list = self.get_parent_list()
for parent in self.parents: for parent in self.parents:
for obj, model in parent._meta.get_all_related_objects_with_model(include_hidden=True): for obj, model in parent._meta.get_all_related_objects_with_model(include_hidden=True):
@ -519,7 +519,7 @@ class Options(object):
return list(six.iteritems(cache)) return list(six.iteritems(cache))
def _fill_related_many_to_many_cache(self): def _fill_related_many_to_many_cache(self):
cache = SortedDict() cache = OrderedDict()
parent_list = self.get_parent_list() parent_list = self.get_parent_list()
for parent in self.parents: for parent in self.parents:
for obj, model in parent._meta.get_all_related_m2m_objects_with_model(): for obj, model in parent._meta.get_all_related_m2m_objects_with_model():

View File

@ -7,9 +7,9 @@ databases). The abstraction barrier only works one way: this module has to know
all about the internals of models in order to get the information it needs. all about the internals of models in order to get the information it needs.
""" """
from collections import OrderedDict
import copy import copy
from django.utils.datastructures import SortedDict
from django.utils.encoding import force_text from django.utils.encoding import force_text
from django.utils.tree import Node from django.utils.tree import Node
from django.utils import six from django.utils import six
@ -142,7 +142,7 @@ class Query(object):
self.select_related = False self.select_related = False
# SQL aggregate-related attributes # SQL aggregate-related attributes
self.aggregates = SortedDict() # Maps alias -> SQL aggregate function self.aggregates = OrderedDict() # Maps alias -> SQL aggregate function
self.aggregate_select_mask = None self.aggregate_select_mask = None
self._aggregate_select_cache = None self._aggregate_select_cache = None
@ -152,7 +152,7 @@ class Query(object):
# These are for extensions. The contents are more or less appended # These are for extensions. The contents are more or less appended
# verbatim to the appropriate clause. # verbatim to the appropriate clause.
self.extra = SortedDict() # Maps col_alias -> (col_sql, params). self.extra = OrderedDict() # Maps col_alias -> (col_sql, params).
self.extra_select_mask = None self.extra_select_mask = None
self._extra_select_cache = None self._extra_select_cache = None
@ -741,7 +741,7 @@ class Query(object):
self.group_by = [relabel_column(col) for col in self.group_by] self.group_by = [relabel_column(col) for col in self.group_by]
self.select = [SelectInfo(relabel_column(s.col), s.field) self.select = [SelectInfo(relabel_column(s.col), s.field)
for s in self.select] for s in self.select]
self.aggregates = SortedDict( self.aggregates = OrderedDict(
(key, relabel_column(col)) for key, col in self.aggregates.items()) (key, relabel_column(col)) for key, col in self.aggregates.items())
# 2. Rename the alias in the internal table/alias datastructures. # 2. Rename the alias in the internal table/alias datastructures.
@ -795,7 +795,7 @@ class Query(object):
assert current < ord('Z') assert current < ord('Z')
prefix = chr(current + 1) prefix = chr(current + 1)
self.alias_prefix = prefix self.alias_prefix = prefix
change_map = SortedDict() change_map = OrderedDict()
for pos, alias in enumerate(self.tables): for pos, alias in enumerate(self.tables):
if alias in exceptions: if alias in exceptions:
continue continue
@ -1638,7 +1638,7 @@ class Query(object):
# dictionary with their parameters in 'select_params' so that # dictionary with their parameters in 'select_params' so that
# subsequent updates to the select dictionary also adjust the # subsequent updates to the select dictionary also adjust the
# parameters appropriately. # parameters appropriately.
select_pairs = SortedDict() select_pairs = OrderedDict()
if select_params: if select_params:
param_iter = iter(select_params) param_iter = iter(select_params)
else: else:
@ -1651,7 +1651,7 @@ class Query(object):
entry_params.append(next(param_iter)) entry_params.append(next(param_iter))
pos = entry.find("%s", pos + 2) pos = entry.find("%s", pos + 2)
select_pairs[name] = (entry, entry_params) select_pairs[name] = (entry, entry_params)
# This is order preserving, since self.extra_select is a SortedDict. # This is order preserving, since self.extra_select is an OrderedDict.
self.extra.update(select_pairs) self.extra.update(select_pairs)
if where or params: if where or params:
self.where.add(ExtraWhere(where, params), AND) self.where.add(ExtraWhere(where, params), AND)
@ -1760,7 +1760,7 @@ class Query(object):
self._extra_select_cache = None self._extra_select_cache = None
def _aggregate_select(self): def _aggregate_select(self):
"""The SortedDict of aggregate columns that are not masked, and should """The OrderedDict of aggregate columns that are not masked, and should
be used in the SELECT clause. be used in the SELECT clause.
This result is cached for optimization purposes. This result is cached for optimization purposes.
@ -1768,7 +1768,7 @@ class Query(object):
if self._aggregate_select_cache is not None: if self._aggregate_select_cache is not None:
return self._aggregate_select_cache return self._aggregate_select_cache
elif self.aggregate_select_mask is not None: elif self.aggregate_select_mask is not None:
self._aggregate_select_cache = SortedDict([ self._aggregate_select_cache = OrderedDict([
(k, v) for k, v in self.aggregates.items() (k, v) for k, v in self.aggregates.items()
if k in self.aggregate_select_mask if k in self.aggregate_select_mask
]) ])
@ -1781,7 +1781,7 @@ class Query(object):
if self._extra_select_cache is not None: if self._extra_select_cache is not None:
return self._extra_select_cache return self._extra_select_cache
elif self.extra_select_mask is not None: elif self.extra_select_mask is not None:
self._extra_select_cache = SortedDict([ self._extra_select_cache = OrderedDict([
(k, v) for k, v in self.extra.items() (k, v) for k, v in self.extra.items()
if k in self.extra_select_mask if k in self.extra_select_mask
]) ])

View File

@ -4,6 +4,7 @@ Form classes
from __future__ import unicode_literals from __future__ import unicode_literals
from collections import OrderedDict
import copy import copy
import warnings import warnings
@ -11,7 +12,6 @@ from django.core.exceptions import ValidationError
from django.forms.fields import Field, FileField from django.forms.fields import Field, FileField
from django.forms.util import flatatt, ErrorDict, ErrorList from django.forms.util import flatatt, ErrorDict, ErrorList
from django.forms.widgets import Media, media_property, TextInput, Textarea from django.forms.widgets import Media, media_property, TextInput, Textarea
from django.utils.datastructures import SortedDict
from django.utils.html import conditional_escape, format_html from django.utils.html import conditional_escape, format_html
from django.utils.encoding import smart_text, force_text, python_2_unicode_compatible from django.utils.encoding import smart_text, force_text, python_2_unicode_compatible
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
@ -55,7 +55,7 @@ def get_declared_fields(bases, attrs, with_base_fields=True):
if hasattr(base, 'declared_fields'): if hasattr(base, 'declared_fields'):
fields = list(six.iteritems(base.declared_fields)) + fields fields = list(six.iteritems(base.declared_fields)) + fields
return SortedDict(fields) return OrderedDict(fields)
class DeclarativeFieldsMetaclass(type): class DeclarativeFieldsMetaclass(type):
""" """

View File

@ -5,6 +5,7 @@ and database field objects.
from __future__ import unicode_literals from __future__ import unicode_literals
from collections import OrderedDict
import warnings import warnings
from django.core.exceptions import ValidationError, NON_FIELD_ERRORS, FieldError from django.core.exceptions import ValidationError, NON_FIELD_ERRORS, FieldError
@ -15,7 +16,6 @@ from django.forms.util import ErrorList
from django.forms.widgets import (SelectMultiple, HiddenInput, from django.forms.widgets import (SelectMultiple, HiddenInput,
MultipleHiddenInput, media_property, CheckboxSelectMultiple) MultipleHiddenInput, media_property, CheckboxSelectMultiple)
from django.utils.encoding import smart_text, force_text from django.utils.encoding import smart_text, force_text
from django.utils.datastructures import SortedDict
from django.utils import six from django.utils import six
from django.utils.text import get_text_list, capfirst from django.utils.text import get_text_list, capfirst
from django.utils.translation import ugettext_lazy as _, ugettext, string_concat from django.utils.translation import ugettext_lazy as _, ugettext, string_concat
@ -142,7 +142,7 @@ def fields_for_model(model, fields=None, exclude=None, widgets=None,
formfield_callback=None, localized_fields=None, formfield_callback=None, localized_fields=None,
labels=None, help_texts=None, error_messages=None): labels=None, help_texts=None, error_messages=None):
""" """
Returns a ``SortedDict`` containing form fields for the given model. Returns a ``OrderedDict`` containing form fields for the given model.
``fields`` is an optional list of field names. If provided, only the named ``fields`` is an optional list of field names. If provided, only the named
fields will be included in the returned fields. fields will be included in the returned fields.
@ -199,9 +199,9 @@ def fields_for_model(model, fields=None, exclude=None, widgets=None,
field_list.append((f.name, formfield)) field_list.append((f.name, formfield))
else: else:
ignored.append(f.name) ignored.append(f.name)
field_dict = SortedDict(field_list) field_dict = OrderedDict(field_list)
if fields: if fields:
field_dict = SortedDict( field_dict = OrderedDict(
[(f, field_dict.get(f)) for f in fields [(f, field_dict.get(f)) for f in fields
if ((not exclude) or (exclude and f not in exclude)) and (f not in ignored)] if ((not exclude) or (exclude and f not in exclude)) and (f not in ignored)]
) )

View File

@ -1,12 +1,13 @@
"This is the locale selecting middleware that will look at accept headers" "This is the locale selecting middleware that will look at accept headers"
from collections import OrderedDict
from django.conf import settings from django.conf import settings
from django.core.urlresolvers import (is_valid_path, get_resolver, from django.core.urlresolvers import (is_valid_path, get_resolver,
LocaleRegexURLResolver) LocaleRegexURLResolver)
from django.http import HttpResponseRedirect from django.http import HttpResponseRedirect
from django.utils.cache import patch_vary_headers from django.utils.cache import patch_vary_headers
from django.utils import translation from django.utils import translation
from django.utils.datastructures import SortedDict
class LocaleMiddleware(object): class LocaleMiddleware(object):
@ -19,7 +20,7 @@ class LocaleMiddleware(object):
""" """
def __init__(self): def __init__(self):
self._supported_languages = SortedDict(settings.LANGUAGES) self._supported_languages = OrderedDict(settings.LANGUAGES)
self._is_language_prefix_patterns_used = False self._is_language_prefix_patterns_used = False
for url_pattern in get_resolver(None).url_patterns: for url_pattern in get_resolver(None).url_patterns:
if isinstance(url_pattern, LocaleRegexURLResolver): if isinstance(url_pattern, LocaleRegexURLResolver):

View File

@ -1,7 +1,7 @@
import copy import copy
import warnings
from django.utils import six from django.utils import six
class MergeDict(object): class MergeDict(object):
""" """
A simple class for creating new "virtual" dictionaries that actually look A simple class for creating new "virtual" dictionaries that actually look
@ -124,6 +124,10 @@ class SortedDict(dict):
return instance return instance
def __init__(self, data=None): def __init__(self, data=None):
warnings.warn(
"SortedDict is deprecated and will be removed in Django 1.9.",
PendingDeprecationWarning, stacklevel=2
)
if data is None or isinstance(data, dict): if data is None or isinstance(data, dict):
data = data or [] data = data or []
super(SortedDict, self).__init__(data) super(SortedDict, self).__init__(data)

View File

@ -1,6 +1,7 @@
"""Translation helper functions.""" """Translation helper functions."""
from __future__ import unicode_literals from __future__ import unicode_literals
from collections import OrderedDict
import locale import locale
import os import os
import re import re
@ -10,7 +11,6 @@ from importlib import import_module
from threading import local from threading import local
import warnings import warnings
from django.utils.datastructures import SortedDict
from django.utils.encoding import force_str, force_text from django.utils.encoding import force_str, force_text
from django.utils.functional import memoize from django.utils.functional import memoize
from django.utils._os import upath from django.utils._os import upath
@ -369,7 +369,7 @@ def get_supported_language_variant(lang_code, supported=None, strict=False):
""" """
if supported is None: if supported is None:
from django.conf import settings from django.conf import settings
supported = SortedDict(settings.LANGUAGES) supported = OrderedDict(settings.LANGUAGES)
if lang_code: if lang_code:
# if fr-CA is not supported, try fr-ca; if that fails, fallback to fr. # if fr-CA is not supported, try fr-ca; if that fails, fallback to fr.
generic_lang_code = lang_code.split('-')[0] generic_lang_code = lang_code.split('-')[0]
@ -396,7 +396,7 @@ def get_language_from_path(path, supported=None, strict=False):
""" """
if supported is None: if supported is None:
from django.conf import settings from django.conf import settings
supported = SortedDict(settings.LANGUAGES) supported = OrderedDict(settings.LANGUAGES)
regex_match = language_code_prefix_re.match(path) regex_match = language_code_prefix_re.match(path)
if not regex_match: if not regex_match:
return None return None
@ -418,7 +418,7 @@ def get_language_from_request(request, check_path=False):
""" """
global _accepted global _accepted
from django.conf import settings from django.conf import settings
supported = SortedDict(settings.LANGUAGES) supported = OrderedDict(settings.LANGUAGES)
if check_path: if check_path:
lang_code = get_language_from_path(request.path_info, supported) lang_code = get_language_from_path(request.path_info, supported)

View File

@ -423,6 +423,9 @@ these changes.
* FastCGI support via the ``runfcgi`` management command will be * FastCGI support via the ``runfcgi`` management command will be
removed. Please deploy your project using WSGI. removed. Please deploy your project using WSGI.
* ``django.utils.datastructures.SortedDict`` will be removed. Use
:class:`collections.OrderedDict` from the Python standard library instead.
2.0 2.0
--- ---

View File

@ -977,14 +977,13 @@ of the arguments is required, but you should use at least one of them.
``select_params`` parameter. Since ``select_params`` is a sequence and ``select_params`` parameter. Since ``select_params`` is a sequence and
the ``select`` attribute is a dictionary, some care is required so that the ``select`` attribute is a dictionary, some care is required so that
the parameters are matched up correctly with the extra select pieces. the parameters are matched up correctly with the extra select pieces.
In this situation, you should use a In this situation, you should use a :class:`collections.OrderedDict` for
:class:`django.utils.datastructures.SortedDict` for the ``select`` the ``select`` value, not just a normal Python dictionary.
value, not just a normal Python dictionary.
This will work, for example:: This will work, for example::
Blog.objects.extra( Blog.objects.extra(
select=SortedDict([('a', '%s'), ('b', '%s')]), select=OrderedDict([('a', '%s'), ('b', '%s')]),
select_params=('one', 'two')) select_params=('one', 'two'))
The only thing to be careful about when using select parameters in The only thing to be careful about when using select parameters in

View File

@ -105,6 +105,10 @@ to distinguish caches by the ``Accept-language`` header.
.. class:: SortedDict .. class:: SortedDict
.. deprecated:: 1.7
``SortedDict`` is deprecated and will be removed in Django 1.9. Use
:class:`collections.OrderedDict` instead.
The :class:`django.utils.datastructures.SortedDict` class is a dictionary The :class:`django.utils.datastructures.SortedDict` class is a dictionary
that keeps its keys in the order in which they're inserted. that keeps its keys in the order in which they're inserted.

View File

@ -167,6 +167,13 @@ on all Python versions. Since ``unittest2`` became the standard library's
Python versions, this module isn't useful anymore. It has been deprecated. Use Python versions, this module isn't useful anymore. It has been deprecated. Use
:mod:`unittest` instead. :mod:`unittest` instead.
``django.utils.datastructures.SortedDict``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
As :class:`~collections.OrderedDict` was added to the standard library in
Python 2.7, :class:`~django.utils.datastructures.SortedDict` is no longer
needed and has been deprecated.
Custom SQL location for models package Custom SQL location for models package
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -1,10 +1,10 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from collections import OrderedDict
import datetime import datetime
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.test import TestCase from django.test import TestCase
from django.utils.datastructures import SortedDict
from .models import TestObject, Order, RevisionableModel from .models import TestObject, Order, RevisionableModel
@ -74,7 +74,7 @@ class ExtraRegressTests(TestCase):
# select portions. Applies when portions are updated or otherwise # select portions. Applies when portions are updated or otherwise
# moved around. # moved around.
qs = User.objects.extra( qs = User.objects.extra(
select=SortedDict((("alpha", "%s"), ("beta", "2"), ("gamma", "%s"))), select=OrderedDict((("alpha", "%s"), ("beta", "2"), ("gamma", "%s"))),
select_params=(1, 3) select_params=(1, 3)
) )
qs = qs.extra(select={"beta": 4}) qs = qs.extra(select={"beta": 4})
@ -180,100 +180,100 @@ class ExtraRegressTests(TestCase):
obj.save() obj.save()
self.assertEqual( self.assertEqual(
list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values()), list(TestObject.objects.extra(select=OrderedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values()),
[{'bar': 'second', 'third': 'third', 'second': 'second', 'whiz': 'third', 'foo': 'first', 'id': obj.pk, 'first': 'first'}] [{'bar': 'second', 'third': 'third', 'second': 'second', 'whiz': 'third', 'foo': 'first', 'id': obj.pk, 'first': 'first'}]
) )
# Extra clauses after an empty values clause are still included # Extra clauses after an empty values clause are still included
self.assertEqual( self.assertEqual(
list(TestObject.objects.values().extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third'))))), list(TestObject.objects.values().extra(select=OrderedDict((('foo','first'), ('bar','second'), ('whiz','third'))))),
[{'bar': 'second', 'third': 'third', 'second': 'second', 'whiz': 'third', 'foo': 'first', 'id': obj.pk, 'first': 'first'}] [{'bar': 'second', 'third': 'third', 'second': 'second', 'whiz': 'third', 'foo': 'first', 'id': obj.pk, 'first': 'first'}]
) )
# Extra columns are ignored if not mentioned in the values() clause # Extra columns are ignored if not mentioned in the values() clause
self.assertEqual( self.assertEqual(
list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values('first', 'second')), list(TestObject.objects.extra(select=OrderedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values('first', 'second')),
[{'second': 'second', 'first': 'first'}] [{'second': 'second', 'first': 'first'}]
) )
# Extra columns after a non-empty values() clause are ignored # Extra columns after a non-empty values() clause are ignored
self.assertEqual( self.assertEqual(
list(TestObject.objects.values('first', 'second').extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third'))))), list(TestObject.objects.values('first', 'second').extra(select=OrderedDict((('foo','first'), ('bar','second'), ('whiz','third'))))),
[{'second': 'second', 'first': 'first'}] [{'second': 'second', 'first': 'first'}]
) )
# Extra columns can be partially returned # Extra columns can be partially returned
self.assertEqual( self.assertEqual(
list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values('first', 'second', 'foo')), list(TestObject.objects.extra(select=OrderedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values('first', 'second', 'foo')),
[{'second': 'second', 'foo': 'first', 'first': 'first'}] [{'second': 'second', 'foo': 'first', 'first': 'first'}]
) )
# Also works if only extra columns are included # Also works if only extra columns are included
self.assertEqual( self.assertEqual(
list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values('foo', 'whiz')), list(TestObject.objects.extra(select=OrderedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values('foo', 'whiz')),
[{'foo': 'first', 'whiz': 'third'}] [{'foo': 'first', 'whiz': 'third'}]
) )
# Values list works the same way # Values list works the same way
# All columns are returned for an empty values_list() # All columns are returned for an empty values_list()
self.assertEqual( self.assertEqual(
list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list()), list(TestObject.objects.extra(select=OrderedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list()),
[('first', 'second', 'third', obj.pk, 'first', 'second', 'third')] [('first', 'second', 'third', obj.pk, 'first', 'second', 'third')]
) )
# Extra columns after an empty values_list() are still included # Extra columns after an empty values_list() are still included
self.assertEqual( self.assertEqual(
list(TestObject.objects.values_list().extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third'))))), list(TestObject.objects.values_list().extra(select=OrderedDict((('foo','first'), ('bar','second'), ('whiz','third'))))),
[('first', 'second', 'third', obj.pk, 'first', 'second', 'third')] [('first', 'second', 'third', obj.pk, 'first', 'second', 'third')]
) )
# Extra columns ignored completely if not mentioned in values_list() # Extra columns ignored completely if not mentioned in values_list()
self.assertEqual( self.assertEqual(
list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('first', 'second')), list(TestObject.objects.extra(select=OrderedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('first', 'second')),
[('first', 'second')] [('first', 'second')]
) )
# Extra columns after a non-empty values_list() clause are ignored completely # Extra columns after a non-empty values_list() clause are ignored completely
self.assertEqual( self.assertEqual(
list(TestObject.objects.values_list('first', 'second').extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third'))))), list(TestObject.objects.values_list('first', 'second').extra(select=OrderedDict((('foo','first'), ('bar','second'), ('whiz','third'))))),
[('first', 'second')] [('first', 'second')]
) )
self.assertEqual( self.assertEqual(
list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('second', flat=True)), list(TestObject.objects.extra(select=OrderedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('second', flat=True)),
['second'] ['second']
) )
# Only the extra columns specified in the values_list() are returned # Only the extra columns specified in the values_list() are returned
self.assertEqual( self.assertEqual(
list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('first', 'second', 'whiz')), list(TestObject.objects.extra(select=OrderedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('first', 'second', 'whiz')),
[('first', 'second', 'third')] [('first', 'second', 'third')]
) )
# ...also works if only extra columns are included # ...also works if only extra columns are included
self.assertEqual( self.assertEqual(
list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('foo','whiz')), list(TestObject.objects.extra(select=OrderedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('foo','whiz')),
[('first', 'third')] [('first', 'third')]
) )
self.assertEqual( self.assertEqual(
list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('whiz', flat=True)), list(TestObject.objects.extra(select=OrderedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('whiz', flat=True)),
['third'] ['third']
) )
# ... and values are returned in the order they are specified # ... and values are returned in the order they are specified
self.assertEqual( self.assertEqual(
list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('whiz','foo')), list(TestObject.objects.extra(select=OrderedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('whiz','foo')),
[('third', 'first')] [('third', 'first')]
) )
self.assertEqual( self.assertEqual(
list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('first','id')), list(TestObject.objects.extra(select=OrderedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('first','id')),
[('first', obj.pk)] [('first', obj.pk)]
) )
self.assertEqual( self.assertEqual(
list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('whiz', 'first', 'bar', 'id')), list(TestObject.objects.extra(select=OrderedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('whiz', 'first', 'bar', 'id')),
[('third', 'first', 'second', obj.pk)] [('third', 'first', 'second', obj.pk)]
) )

View File

@ -1,5 +1,6 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from collections import OrderedDict
import datetime import datetime
from operator import attrgetter from operator import attrgetter
import pickle import pickle
@ -14,7 +15,6 @@ from django.db.models.sql.where import WhereNode, EverythingNode, NothingNode
from django.db.models.sql.datastructures import EmptyResultSet from django.db.models.sql.datastructures import EmptyResultSet
from django.test import TestCase, skipUnlessDBFeature from django.test import TestCase, skipUnlessDBFeature
from django.test.utils import str_prefix from django.test.utils import str_prefix
from django.utils.datastructures import SortedDict
from .models import ( from .models import (
Annotation, Article, Author, Celebrity, Child, Cover, Detail, DumbCategory, Annotation, Article, Author, Celebrity, Child, Cover, Detail, DumbCategory,
@ -499,7 +499,7 @@ class Queries1Tests(BaseQuerysetTest):
) )
def test_ticket2902(self): def test_ticket2902(self):
# Parameters can be given to extra_select, *if* you use a SortedDict. # 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 # (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 # your system, so we can put things in the wrong way around from
@ -513,7 +513,7 @@ class Queries1Tests(BaseQuerysetTest):
# This slightly odd comparison works around the fact that PostgreSQL will # This slightly odd comparison works around the fact that PostgreSQL will
# return 'one' and 'two' as strings, not Unicode objects. It's a side-effect of # return 'one' and 'two' as strings, not Unicode objects. It's a side-effect of
# using constants here and not a real concern. # using constants here and not a real concern.
d = Item.objects.extra(select=SortedDict(s), select_params=params).values('a', 'b')[0] d = Item.objects.extra(select=OrderedDict(s), select_params=params).values('a', 'b')[0]
self.assertEqual(d, {'a': 'one', 'b': 'two'}) self.assertEqual(d, {'a': 'one', 'b': 'two'})
# Order by the number of tags attached to an item. # Order by the number of tags attached to an item.
@ -1987,7 +1987,7 @@ class ValuesQuerysetTests(BaseQuerysetTest):
def test_extra_values(self): def test_extra_values(self):
# testing for ticket 14930 issues # testing for ticket 14930 issues
qs = Number.objects.extra(select=SortedDict([('value_plus_x', 'num+%s'), qs = Number.objects.extra(select=OrderedDict([('value_plus_x', 'num+%s'),
('value_minus_x', 'num-%s')]), ('value_minus_x', 'num-%s')]),
select_params=(1, 2)) select_params=(1, 2))
qs = qs.order_by('value_minus_x') qs = qs.order_by('value_minus_x')