In Python 3.9, PEP 584 added the |
(merge) and |=
(update) operators to dict
and related mapping types.
I propose that we extend MultiValueDict
to support these operators, which will be picked up by its subclass QueryDict
too. That will enable more succinct merge/update patterns, for example to copy and modify request.GET
in a view:
next_page_params = request.GET | {"page": page + 1}
Currently, the operators work due to inheritance from dict
, but with flaws:
|
(merge) always returns a plain dict
.
|=
(update) works even for immutable QueryDict
s, bypassing the mutation block that exists in other methods like update()
.
4 Likes
This feels like a bug to me, so
to fixing it.
That would be a +1 from me
So after a bit of hacking on this branch, I found that it’s a bit tricky to balance backwards compatiblity with correctness for |
.
The reference implementation suggests implementing the operators using .update()
to merge in values. If we do that, it will use MultiValueDict.update()
which extends lists, rather than replacing them. This is counter to the current behaviour which replaces values, so we would break existing code, plus it’s less useful as the example above won’t work: page
would be set to 1 and 2, not replaced.
However, I did find that |=
is actually completely broken right now, as it leaves MultiValueDict
(and QueryDict
) in a broken state:
In [1]: from django.utils.datastructures import MultiValueDict
In [2]: mvd = MultiValueDict({"page": [1]})
In [3]: mvd |= {"page": 2}
In [4]: mvd["page"]
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
Cell In[4], line 1
----> 1 mvd["page"]
File /.../django/utils/datastructures.py:90, in MultiValueDict.__getitem__(self, key)
88 raise MultiValueDictKeyError(key)
89 try:
---> 90 return list_[-1]
91 except IndexError:
92 return []
TypeError: 'int' object is not subscriptable
So I’ll limit this to a bug fix for |=
.