Change QueryDict __getitem__ in case of empty list?

Though ​documentation says that:

QueryDict.__getitem__(key)
Returns the value for the given key. If the key has more than one value, it returns the last value.

It returns a list when the raw value is an empty list:

>>> from django.http import QueryDict
>>> q = QueryDict('a=1', mutable=True)
>>> q['a']
'1'

>>> q.setlist('b', [])
>>> q['b']
[]

which surprises user and it even not mentioned in documentation.

Could we change this behavior? I know that we don’t have a perfect solution, but returning None in this case is less bad than empty list [], because it is easier to annotate type.

  • If returns None, we can annotate type as str | None.
  • If returns [], Python doesn’t have an exact type for empty list, and django-stubs has to annotate as str | list[object] which is quite broader than the actual value (empty list).

The issue has been reported in Django issue tracker, but I’m advised to post here to hear more opinions.

Before changing something, working out why it’s as it is is worthwhile…

Tests added in Migrated regressiontest/datastructures doctest, and moved it into the… · django/django@3610a21 · GitHub — not covering the offending line.

Coverage for that added in Increased test coverage for django.utils.datastructures.MultiValueDict. · django/django@c3d9b8b · GitHub

The issue is the bottom except in MultiValueDict.__getitem__:

What do we do when the list is empty?

Arguably, a correct answer might be to raise a MultiValueDictKeyError as we do above… — after all there is no single item that can be returned — in contrast to getlist(), which would correctly return the empty list.

But, yes, you might opt for None, or the empty list, as was decided since forever.

QueryDict is meant to be instantiated from query strings. I can’t see any way of populating a key with an empty list under normal usage:

>>> q = QueryDict('a=&a=&a=', mutable=True)
>>> q
<QueryDict: {'a': ['', '', '']}>

I think clients using setlist directly should be sensitive to that. (Set [''] in the example you hit, or else del the key if the list is empty.)

Guaranteed folks are relying on the existing behaviour. I don’t think we can change it. The docs change seems the only option.

1 Like