Opened 4 years ago

Closed 4 years ago

Last modified 4 years ago

#32010 closed Bug (invalid)

Unexpected behaviour of MultiValueDict.getlist in conjunction with map

Reported by: Etienne Ott Owned by: nobody
Component: Utilities Version: 3.0
Severity: Normal Keywords:
Cc: Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

When the return value of a call to MultiValueDict.getlist is fed into a map(int, ...) call, the result has unexpected behaviour when checking the existence of elements with the in operator.

Example:

from django.utils.datastructures import MultiValueDict

mvdict = MultiValueDict({"number": "54321"})

mapped = map(int, mvdict.getlist("number"))
if 1 in mapped:
    print("1 is found with mvdict")
else:
    print("1 isn't found with mvdict")

mapped = map(int, mvdict.getlist("number"))
if 54321 in mapped:
    print("54321 is found with mvdict")
else:
    print("54321 isn't found with mvdict")

single_list = ["54321"]

mapped = map(int, single_list)
if 1 in mapped:
    print("1 is found with single list")
else:
    print("1 isn't found with single list")

mapped = map(int, single_list)
if 54321 in mapped:
    print("54321 is found with single list")
else:
    print("54321 isn't found with single list")

Output:

1 is found with mvdict
54321 isn't found with mvdict
1 isn't found with single list
54321 is found with single list

Expected behaviour:
The number 1 is not expected to be found in the list of numbers (which in this example is only one entry), but 54321 is. This assumes that the key of the entry exists in the dict and that the mapping function int neither adds nor removes elements due to being able/not able to parse elements as ints. I'm fairly confident both assumptions are met in the example, but the expected behaviour is not.

Remarks:
I admit this use case may seem a bit strange, but it did "naturally" come up with a form where a user could select some objects identified by their ID and the form, upon submission, checked for the existence of some IDs in the selected list of IDs in order to do something. My suspicion is that some form of string conversion takes place or that an exception is stringified and taken as a return value. Upon checking if an int exists in the variable, python will then check for the existence of an int in the string. This would explain why the number 1 is found, as it is likely to be found, while the number 54321 isn't.

Change History (2)

comment:1 by Carlton Gibson, 4 years ago

Resolution: invalid
Status: newclosed

mvdict = MultiValueDict({"number": "54321"})

This is incorrect usage. If instantiated manually (which is not part of the documented public API) the value of each item should be a list.

Compare:

>>> mvdict = MultiValueDict({"number": "54321"})
>>> mvdict.getlist("number")
['5', '4', '3', '2', '1']
>>> mvdict = MultiValueDict({"number": ["54321"]})
>>> mvdict.getlist("number")
['54321']

Note that the public QueryDict handles this as required:

>>> from django.http import QueryDict
>>> q = QueryDict("number=54321")
>>> q.getlist("number")
['54321']

comment:2 by Etienne Ott, 4 years ago

It seems that when creating the example above I didn't reproduce any kind of bug and instead constructed an incorrect use of MultiValueDict. Further digging has revealed that using the membership operator in on generators like map is also incorrect usage. The pythonic way is to check a list made by list comprehension, e.g. if number in [x for x in map(int, somelist)]. My apologies for wasting time with something unrelated to Django.

Note: See TracTickets for help on using tickets.
Back to Top