Ticket #736: mvd_patch_r1323.diff

File mvd_patch_r1323.diff, 14.7 KB (added by django@…, 19 years ago)

Updated patch for r1323. Further fix for admin app that is currently depending on existing (?incorrect) behaviour of QueryDict rather than documented behaviour. Note that this patch also fixes #709.

  • django/contrib/admin/views/main.py

     
    8686        page_num = 0
    8787    show_all = request.GET.has_key(ALL_VAR)
    8888    is_popup = request.GET.has_key(IS_POPUP_VAR)
    89     params = dict(request.GET.copy())
     89    params = dict((key, value[-1]) for key, value in request.GET.items())
    9090    if params.has_key(PAGE_VAR):
    9191        del params[PAGE_VAR]
    9292    # For ordering, first check the "ordering" parameter in the admin options,
  • django/utils/httpwrappers.py

     
    11from Cookie import SimpleCookie
    22from pprint import pformat
    33from urllib import urlencode
    4 from django.utils import datastructures
     4from django.utils.datastructures import MultiValueDict
    55
    66class HttpRequest(object): # needs to be new-style class because subclasses define "property"s
    77    "A basic HTTP request"
     
    3333    raw_message = '\r\n'.join(['%s:%s' % pair for pair in header_dict.items()])
    3434    raw_message += '\r\n\r\n' + post_data
    3535    msg = email.message_from_string(raw_message)
    36     POST = datastructures.MultiValueDict()
    37     FILES = datastructures.MultiValueDict()
     36    POST = MultiValueDict()
     37    FILES = MultiValueDict()
    3838    for submessage in msg.get_payload():
    3939        if isinstance(submessage, email.Message.Message):
    4040            name_dict = parse_header(submessage['Content-Disposition'])[1]
     
    5757                POST.appendlist(name_dict['name'], submessage.get_payload())
    5858    return POST, FILES
    5959
    60 class QueryDict(datastructures.MultiValueDict):
     60try:
     61    from mod_python.util import parse_qsl
     62except ImportError:
     63    from cgi import parse_qsl
     64class QueryDict(MultiValueDict):
    6165    """A specialized MultiValueDict that takes a query string when initialized.
    6266    This is immutable unless you create a copy of it."""
    6367    def __init__(self, query_string):
    64         try:
    65             from mod_python.util import parse_qsl
    66         except ImportError:
    67             from cgi import parse_qsl
    68         if not query_string:
    69             self.data = {}
    70             self._keys = []
    71         else:
    72             self.data = {}
    73             self._keys = []
    74             for name, value in parse_qsl(query_string, True): # keep_blank_values=True
    75                 if name in self.data:
    76                     self.data[name].append(value)
    77                 else:
    78                     self.data[name] = [value]
    79                 if name not in self._keys:
    80                     self._keys.append(name)
     68        MultiValueDict.__init__(self)
     69        self._mutable = True
     70        for key, value in parse_qsl(query_string, True): # keep_blank_values=True
     71            self.appendlist(key, value)
    8172        self._mutable = False
    8273
    83     def __setitem__(self, key, value):
     74    def _assert_mutable(self):
    8475        if not self._mutable:
    8576            raise AttributeError, "This QueryDict instance is immutable"
    86         else:
    87             self.data[key] = [value]
    88             if not key in self._keys:
    89                 self._keys.append(key)
    9077
     78    def _setitem_if_mutable(self, key, value):
     79        self._assert_mutable()
     80        MultiValueDict.__setitem__(self, key, value)
     81    __setitem__ = _setitem_if_mutable
     82
    9183    def setlist(self, key, list_):
    92         if not self._mutable:
    93             raise AttributeError, "This QueryDict instance is immutable"
    94         else:
    95             self.data[key] = list_
    96             if not key in self._keys:
    97                 self._keys.append(key)
     84        self._assert_mutable()
     85        MultiValueDict.setlist(self, key, list_)
    9886
     87    def appendlist(self, key, value):
     88        self._assert_mutable()
     89        MultiValueDict.appendlist(self, key, value)
     90
     91    def update(self, other_dict):
     92        self._assert_mutable()
     93        MultiValueDict.update(self, other_dict)
     94
     95    def pop(self, key):
     96        self._assert_mutable()
     97        return MultiValueDict.pop(self, key)
     98
     99    def popitem(self):
     100        self._assert_mutable()
     101        return MultiValueDict.popitem(self)
     102
     103    def clear(self):
     104        self._assert_mutable()
     105        MultiValueDict.clear(self)
     106
     107    def setdefault(self, *args):
     108        self._assert_mutable()
     109        return MultiValueDict.setdefault(self, *args)
     110
    99111    def copy(self):
    100         "Returns a mutable copy of this object"
    101         cp = datastructures.MultiValueDict.copy(self)
     112        """Returns a mutable copy of this object.  Our special __setitem__
     113        must be disabled for copying machinery."""
     114        QueryDict.__setitem__ = dict.__setitem__
     115        import copy
     116        cp = copy.deepcopy(self)
     117        QueryDict.__setitem__ = QueryDict._setitem_if_mutable
    102118        cp._mutable = True
    103119        return cp
    104120
    105     def assert_synchronized(self):
    106         assert(len(self._keys) == len(self.data.keys())), \
    107             "QueryDict data structure is out of sync: %s %s" % (str(self._keys), str(self.data))
    108 
    109121    def items(self):
    110         "Respect order preserved by self._keys"
    111         self.assert_synchronized()
    112         items = []
    113         for key in self._keys:
    114             if key in self.data:
    115                 items.append((key, self.data[key][0]))
    116         return items
     122        return self.lists()
    117123
    118     def keys(self):
    119         self.assert_synchronized()
    120         return self._keys
    121 
    122124    def urlencode(self):
    123125        output = []
    124         for k, list_ in self.data.items():
     126        for k, list_ in self.items():
    125127            output.extend([urlencode({k: v}) for v in list_])
    126128        return '&'.join(output)
    127129
  • django/utils/datastructures.py

     
    4343class MultiValueDictKeyError(KeyError):
    4444    pass
    4545
    46 class MultiValueDict:
     46class MultiValueDict(dict):
    4747    """
    48     A dictionary-like class customized to deal with multiple values for the same key.
     48    A subclass of dictionary customized to handle multiple values for the same key.
    4949
    5050    >>> d = MultiValueDict({'name': ['Adrian', 'Simon'], 'position': ['Developer']})
    5151    >>> d['name']
     
    6060    which returns a list for every key, even though most Web forms submit
    6161    single name-value pairs.
    6262    """
    63     def __init__(self, key_to_list_mapping=None):
    64         self.data = key_to_list_mapping or {}
     63    def __init__(self, key_to_list_mapping=()):
     64        dict.__init__(self, key_to_list_mapping)
    6565
    66     def __repr__(self):
    67         return repr(self.data)
    68 
    6966    def __getitem__(self, key):
    70         "Returns the data value for this key; raises KeyError if not found"
    71         if self.data.has_key(key):
    72             try:
    73                 return self.data[key][-1] # in case of duplicates, use last value ([-1])
    74             except IndexError:
    75                 return []
     67        """Returns the last data value for this key or [] if empty list; raises KeyError
     68        if key not found."""
     69        try:
     70            return dict.__getitem__(self, key)[-1]
     71        except IndexError:
     72            return []
    7673        raise MultiValueDictKeyError, "Key '%s' not found in MultiValueDict %s" % (key, self.data)
    7774
    78     def __setitem__(self, key, value):
    79         self.data[key] = [value]
     75    def _setitem_list(self, key, value):
     76        "Items are assigned to a list."
     77        dict.__setitem__(self, key, [value])
     78    __setitem__ = _setitem_list
    8079
    81     def __len__(self):
    82         return len(self.data)
    83 
    84     def get(self, key, default):
    85         "Returns the default value if the requested data doesn't exist"
     80    def get(self, key, *args):
     81        "Returns the default value if the requested data doesn't exist."
    8682        try:
    87             val = self[key]
    88         except (KeyError, IndexError):
    89             return default
    90         if val == []:
    91             return default
    92         return val
     83            return self[key]
     84        except KeyError:
     85            if args:
     86                return args[0]
     87            raise
    9388
    94     def getlist(self, key):
    95         "Returns an empty list if the requested data doesn't exist"
     89    def getlist(self, key, *args):
     90        """Returns an empty list, or a user-specified default, if the requested data
     91        doesn't exist."""
    9692        try:
    97             return self.data[key]
     93            return dict.__getitem__(self, key)
    9894        except KeyError:
     95            if args:
     96                return args[0]
    9997            return []
    10098
    10199    def setlist(self, key, list_):
    102         self.data[key] = list_
     100        "Set the list associated with a key."
     101        dict.__setitem__(self, key, list_)
    103102
    104     def appendlist(self, key, item):
    105         "Appends an item to the internal list associated with key"
    106         try:
    107             self.data[key].append(item)
    108         except KeyError:
    109             self.data[key] = [item]
     103    def appendlist(self, key, value):
     104        "Appends an item to the internal list associated with key."
     105        x = self.setdefault(key, [])
     106        x.append(value)
    110107
    111     def has_key(self, key):
    112         return self.data.has_key(key)
    113 
    114108    def items(self):
    115         # we don't just return self.data.items() here, because we want to use
    116         # self.__getitem__() to access the values as *strings*, not lists
    117         return [(key, self[key]) for key in self.data.keys()]
     109        """Returns a list of (key, value) pairs, where value is the last item in
     110        the list associated with the key."""
     111        return [(key, self[key]) for key in self.keys()]
    118112
    119     def keys(self):
    120         return self.data.keys()
     113    def lists(self):
     114        "Returns a list of (key, list) pairs."
     115        return dict.items(self)
    121116
    122     def update(self, other_dict):
    123         if isinstance(other_dict, MultiValueDict):
    124             for key, value_list in other_dict.data.items():
    125                 self.data.setdefault(key, []).extend(value_list)
    126         elif type(other_dict) == type({}):
    127             for key, value in other_dict.items():
    128                 self.data.setdefault(key, []).append(value)
    129         else:
    130             raise ValueError, "MultiValueDict.update() takes either a MultiValueDict or dictionary"
     117    def values(self):
     118        "Returns a list of the last value on every key list."
     119        return [self[key] for key in self.keys()]
    131120
    132121    def copy(self):
    133         "Returns a copy of this object"
     122        """Returns a copy of this object.  Our special __setitem__
     123        must be disabled for copying machinery."""
    134124        import copy
     125        MultiValueDict.__setitem__ = dict.__setitem__
    135126        cp = copy.deepcopy(self)
     127        MultiValueDict.__setitem__ = MultiValueDict._setitem_list
    136128        return cp
    137129
     130    def update(self, other_dict):
     131        "Update extends rather than replaces existing key lists."
     132        if isinstance(other_dict, MultiValueDict):
     133            for key, value_list in other_dict.lists():
     134                self.setdefault(key, []).extend(value_list)
     135        else:
     136            try:
     137                for key, value in other_dict.items():
     138                    self.setdefault(key, []).append(value)
     139            except TypeError:
     140                raise ValueError, "MultiValueDict.update() takes either a MultiValueDict or dictionary"
     141
    138142class DotExpandedDict(dict):
    139143    """
    140144    A special dictionary constructor that takes a dictionary in which the keys
  • django/core/meta/__init__.py

     
    15781578        # [('0', {'id': ['940'], 'choice': ['This is the first choice']}),
    15791579        #  ('1', {'id': ['941'], 'choice': ['This is the second choice']}),
    15801580        #  ('2', {'id': [''], 'choice': ['']})]
    1581         obj_list = DotExpandedDict(new_data.data)[rel_opts.object_name.lower()].items()
     1581        obj_list = DotExpandedDict(dict(new_data))[rel_opts.object_name.lower()].items()
    15821582        obj_list.sort(lambda x, y: cmp(int(x[0]), int(y[0])))
    15831583        params = {}
    15841584
  • tests/othertests/datastructures.py

     
     1"""
     2>>> d = MultiValueDict({'name': ['Adrian', 'Simon'], 'position': ['Developer']})
     3>>> d['name']
     4'Simon'
     5>>> d.get('name')
     6'Simon'
     7>>> d.get('nonkey')
     8Traceback (most recent call last):
     9    ...
     10KeyError: 'nonkey'
     11>>> d.get('nonkey', 'nonexistent')
     12'nonexistent'
     13>>> d.items()
     14[('position', 'Developer'), ('name', 'Simon')]
     15>>> d.values()
     16['Developer', 'Simon']
     17>>> d.lists()
     18[('position', ['Developer']), ('name', ['Adrian', 'Simon'])]
     19>>> d.appendlist('name', 'Wilson')
     20>>> d.getlist('name')
     21['Adrian', 'Simon', 'Wilson']
     22>>> d.getlist('nonkey')
     23[]
     24>>> d.getlist('nonkey', 'nonexistent')
     25'nonexistent'
     26>>> d.setlist('lastname', ['Holovaty', 'Willison'])
     27>>> d.getlist('lastname')
     28['Holovaty', 'Willison']
     29>>> d.update({'name':'Jacob'})
     30>>> d.getlist('name')
     31['Adrian', 'Simon', 'Wilson', 'Jacob']
     32>>> dc = d.copy()
     33>>> d is dc
     34False
     35>>> d == dc
     36True
     37
     38>>> qd = QueryDict('a=1&b=2&b=3')
     39>>> qd.urlencode()
     40'a=1&b=2&b=3'
     41>>> qd['a']
     42'1'
     43>>> qd.keys()
     44['a', 'b']
     45>>> qd.items()
     46[('a', ['1']), ('b', ['2', '3'])]
     47>>> qd.values()
     48['1', '3']
     49>>> qd['a'] = 2
     50Traceback (most recent call last):
     51    ...
     52AttributeError: This QueryDict instance is immutable
     53>>> qd.setlist('a', [2])
     54Traceback (most recent call last):
     55    ...
     56AttributeError: This QueryDict instance is immutable
     57>>> qd.appendlist('a', 2)
     58Traceback (most recent call last):
     59    ...
     60AttributeError: This QueryDict instance is immutable
     61>>> qd.pop('a')
     62Traceback (most recent call last):
     63    ...
     64AttributeError: This QueryDict instance is immutable
     65>>> qd.popitem()
     66Traceback (most recent call last):
     67    ...
     68AttributeError: This QueryDict instance is immutable
     69>>> qd.update({'a':'2'})
     70Traceback (most recent call last):
     71    ...
     72AttributeError: This QueryDict instance is immutable
     73>>> qd.setdefault('a', 2)
     74Traceback (most recent call last):
     75    ...
     76AttributeError: This QueryDict instance is immutable
     77>>> qd.clear()
     78Traceback (most recent call last):
     79    ...
     80AttributeError: This QueryDict instance is immutable
     81>>> qdc = qd.copy()
     82>>> qd is qdc
     83False
     84>>> qd == qdc
     85True
     86>>> qdc.setlist('a', [1, 2])
     87>>> qdc['a']
     882
     89>>> qdc.appendlist('a', 3)
     90>>> qdc.pop('a')
     91[1, 2, 3]
     92>>> qdc.clear()
     93>>> qdc.setdefault('a', ['1'])
     94['1']
     95>>> qdc.update({'a':'2', 'b':'3'})
     96>>> qdc
     97{'a': ['1', '2'], 'b': ['3']}
     98"""
     99
     100from django.utils.datastructures import MultiValueDict
     101from django.utils.httpwrappers import QueryDict
     102
     103if __name__ == '__main__':
     104    import doctest
     105    doctest.testmod()
Back to Top