Fixed #27198 -- Made MultiValueDict.getlist() return a new list to prevent mutation.
This commit is contained in:
parent
6989b45c8d
commit
727d7ce6cb
|
@ -109,7 +109,7 @@ class MultiValueDict(dict):
|
||||||
|
|
||||||
def __getstate__(self):
|
def __getstate__(self):
|
||||||
obj_dict = self.__dict__.copy()
|
obj_dict = self.__dict__.copy()
|
||||||
obj_dict['_data'] = {k: self.getlist(k) for k in self}
|
obj_dict['_data'] = {k: self._getlist(k) for k in self}
|
||||||
return obj_dict
|
return obj_dict
|
||||||
|
|
||||||
def __setstate__(self, obj_dict):
|
def __setstate__(self, obj_dict):
|
||||||
|
@ -131,17 +131,30 @@ class MultiValueDict(dict):
|
||||||
return default
|
return default
|
||||||
return val
|
return val
|
||||||
|
|
||||||
def getlist(self, key, default=None):
|
def _getlist(self, key, default=None, force_list=False):
|
||||||
"""
|
"""
|
||||||
Returns the list of values for the passed key. If key doesn't exist,
|
Return a list of values for the key.
|
||||||
then a default value is returned.
|
|
||||||
|
Used internally to manipulate values list. If force_list is True,
|
||||||
|
return a new copy of values.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
return super(MultiValueDict, self).__getitem__(key)
|
values = super(MultiValueDict, self).__getitem__(key)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
if default is None:
|
if default is None:
|
||||||
return []
|
return []
|
||||||
return default
|
return default
|
||||||
|
else:
|
||||||
|
if force_list:
|
||||||
|
values = list(values)
|
||||||
|
return values
|
||||||
|
|
||||||
|
def getlist(self, key, default=None):
|
||||||
|
"""
|
||||||
|
Return the list of values for the key. If key doesn't exist, return a
|
||||||
|
default value.
|
||||||
|
"""
|
||||||
|
return self._getlist(key, default, force_list=True)
|
||||||
|
|
||||||
def setlist(self, key, list_):
|
def setlist(self, key, list_):
|
||||||
super(MultiValueDict, self).__setitem__(key, list_)
|
super(MultiValueDict, self).__setitem__(key, list_)
|
||||||
|
@ -160,7 +173,7 @@ class MultiValueDict(dict):
|
||||||
self.setlist(key, default_list)
|
self.setlist(key, default_list)
|
||||||
# Do not return default_list here because setlist() may store
|
# Do not return default_list here because setlist() may store
|
||||||
# another value -- QueryDict.setlist() does. Look it up.
|
# another value -- QueryDict.setlist() does. Look it up.
|
||||||
return self.getlist(key)
|
return self._getlist(key)
|
||||||
|
|
||||||
def appendlist(self, key, value):
|
def appendlist(self, key, value):
|
||||||
"""Appends an item to the internal list associated with key."""
|
"""Appends an item to the internal list associated with key."""
|
||||||
|
|
|
@ -101,6 +101,24 @@ class MultiValueDictTests(SimpleTestCase):
|
||||||
|
|
||||||
self.assertEqual({}, MultiValueDict().dict())
|
self.assertEqual({}, MultiValueDict().dict())
|
||||||
|
|
||||||
|
def test_getlist_doesnt_mutate(self):
|
||||||
|
x = MultiValueDict({'a': ['1', '2'], 'b': ['3']})
|
||||||
|
values = x.getlist('a')
|
||||||
|
values += x.getlist('b')
|
||||||
|
self.assertEqual(x.getlist('a'), ['1', '2'])
|
||||||
|
|
||||||
|
def test_internal_getlist_does_mutate(self):
|
||||||
|
x = MultiValueDict({'a': ['1', '2'], 'b': ['3']})
|
||||||
|
values = x._getlist('a')
|
||||||
|
values += x._getlist('b')
|
||||||
|
self.assertEqual(x._getlist('a'), ['1', '2', '3'])
|
||||||
|
|
||||||
|
def test_getlist_default(self):
|
||||||
|
x = MultiValueDict({'a': [1]})
|
||||||
|
MISSING = object()
|
||||||
|
values = x.getlist('b', default=MISSING)
|
||||||
|
self.assertIs(values, MISSING)
|
||||||
|
|
||||||
|
|
||||||
class ImmutableListTests(SimpleTestCase):
|
class ImmutableListTests(SimpleTestCase):
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue