Opened 5 weeks ago

Closed 3 weeks ago

#35915 closed Cleanup/optimization (fixed)

QueryDict __getitem__ returns an empty list when the value is an empty list

Reported by: Nguyễn Hồng Quân Owned by: jburns6789
Component: HTTP handling Version: 5.1
Severity: Normal Keywords: querydict
Cc: Triage Stage: Ready for checkin
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description (last modified by David Sanders)

Update: forum post: https://forum.djangoproject.com/t/change-querydict-getitem-in-case-of-empty-list/36522

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).

Change History (16)

comment:1 by Sarah Boyce, 5 weeks ago

Summary: QueryDict __getitem__ returns list which is surpisingQueryDict __getitem__ returns an empty list when the value is an empty list
Triage Stage: UnreviewedAccepted

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.

It's been this way a very long time, I'm not sure what the implications of changing it would be (in terms of compatibility). I also think changing it for easier type annotations is not a strong argument here. You could raise this topic on the Django forum and get others opinions if you wish to change it.

That being said, I think a docs adjustment is required (note that the docstring states Return the last data value for this key, or [] if it's an empty list; raise KeyError if not found.).
Accepting on that basis.

comment:2 by jburns6789, 5 weeks ago

Owner: set to jburns6789
Status: newassigned

comment:3 by jburns6789, 5 weeks ago

Ok, looking over your suggestion,
I'm trying to figure out what I need to do, here is the doc section for QuerySet

https://docs.djangoproject.com/en/5.1/ref/request-response/#django.http.QueryDict

QueryDict.getitem(key)¶
Returns the value for the given key. If the key has more than one value, it returns the last value. Raises django.utils.datastructures.MultiValueDictKeyError if the key does not exist. (This is a subclass of Python’s standard KeyError, so you can stick to catching KeyError.)

I need to state when a QueryDictkey returns [ ], the said key is associated with a empty list?

comment:4 by Ayush Khatri , 5 weeks ago

Owner: changed from jburns6789 to Ayush Khatri

in reply to:  3 comment:5 by Sarah Boyce, 5 weeks ago

Replying to jburns6789:

QueryDict.getitem(key)¶
Returns the value for the given key. If the key has more than one value, it returns the last value. Raises django.utils.datastructures.MultiValueDictKeyError if the key does not exist. (This is a subclass of Python’s standard KeyError, so you can stick to catching KeyError.)

I need to state when a QueryDictkey returns [ ], the said key is associated with a empty list?

Yes
I would be tempted to update this to be very similar to the doc string so maybe

  • docs/ref/request-response.txt

    diff --git a/docs/ref/request-response.txt b/docs/ref/request-response.txt
    index afebd00d8b..abdf75984d 100644
    a b a subclass of dictionary. Exceptions are outlined here:  
    554554
    555555.. method:: QueryDict.__getitem__(key)
    556556
    557     Returns the value for the given key. If the key has more than one value,
    558     it returns the last value. Raises
    559     ``django.utils.datastructures.MultiValueDictKeyError`` if the key does not
    560     exist. (This is a subclass of Python's standard :exc:`KeyError`, so you can
    561     stick to catching ``KeyError``.)
     557    Returns the last data value for the given key, or ``[]`` if it's an empty
     558    list. Raises ``django.utils.datastructures.MultiValueDictKeyError`` if the
     559    key does not exist. (This is a subclass of Python's standard
     560    :exc:`KeyError`, so you can stick to catching ``KeyError``.)
    562561
    563562.. method:: QueryDict.__setitem__(key, value)

comment:6 by Sarah Boyce, 5 weeks ago

Owner: changed from Ayush Khatri to jburns6789

comment:7 by jburns6789, 4 weeks ago

Hi, here is my link to the PR, someone else was reviewing, is there anything I need to in the ticket tracker?

comment:9 by jburns6789, 4 weeks ago

Has patch: set

comment:10 by David Sanders, 4 weeks ago

Description: modified (diff)

in reply to:  10 comment:11 by jburns6789, 4 weeks ago

Updated requested information https://github.com/django/django/pull/18841

comment:12 by jburns6789, 4 weeks ago

Is there anything else needed to be completed? or do I keep checking for someone to approve/deny the merging of the PR?

comment:13 by Tim Graham, 4 weeks ago

The yellow "According to the ticket's flags" box below the ticket description explains the next steps.

comment:14 by Sarah Boyce, 4 weeks ago

Patch needs improvement: set

comment:15 by Sarah Boyce, 3 weeks ago

Patch needs improvement: unset
Triage Stage: AcceptedReady for checkin

comment:16 by Sarah Boyce <42296566+sarahboyce@…>, 3 weeks ago

Resolution: fixed
Status: assignedclosed

In b8f9f625:

Fixed #35915 -- Clarified the empty list case in QueryDict.getitem() docs.

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