Code

Opened 6 years ago

Closed 6 years ago

Last modified 3 years ago

#8278 closed (fixed)

QueryDict.update eats data when other_dict is a MultiValueDict

Reported by: jdunck Owned by: nobody
Component: Core (Other) Version: master
Severity: Keywords:
Cc: Triage Stage: Accepted
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: yes
Easy pickings: UI/UX:

Description

django.http.QueryDict supports .update, but it seems to assume that other_dict is a (builtin) dict.

This is dangerous when mixing two QueryDicts.

>>> x = QueryDict('a=1&a=2')
>>> y = QueryDict('a=3&a=4')
>>> print x
<QueryDict: {u'a': [u'1', u'2']}>
>>> print y
<QueryDict: {u'a': [u'3', u'4']}>
>>> x._mutable = True
>>> x.update(y)
>>> x._mutable=False
>>> print x
<QueryDict: {u'a': [u'1', u'2', u'4']}>

Note that a=3 was lost.

As far as I can see, there's no really nice fix to this. QueryDict.update doesn't just defer to MultiValueDict because it needs to force keys and values into unicode.

Defering to MultiValueDict first and then forcing means duplicate keys for items not yet coerced.
Forcing individual items results in lists from QueryDict becoming (single) items in MultiValueDict.

i.e.

dict([(f(k), f(v)) for k, v in other_dict.items()])
->
dict([(f(k), [f(v) for v in values]) for k, values in other_dict.lists()])

This seems to be a leaky abstraction here; I think QueryDict is not really a MultiValueDict, since MultiValueDict can have any (hashable) objects as keys and values, while QueryDict insists on unicode objects only.

No patch from me since the solution isn't obvious to me. Marking as 1.0 since this is a bug, though.

Attachments (2)

8278.querydict.diff (2.3 KB) - added by julien 6 years ago.
Patch and regression tests
8278.querydict.2.diff (1.7 KB) - added by julien 6 years ago.
2nd attempt. All tests pass.

Download all attachments as: .zip

Change History (10)

Changed 6 years ago by julien

Patch and regression tests

comment:1 Changed 6 years ago by julien

I think there is a combination of two issues here:

  • dict eats duplicate keys:
>>> dict([(u'a', u'1'), (u'a', u'2')])
dict: {u'a': u'2'} 
  • And MultiValueDict is not prepared to receive values as lists.

Hopefully the attached patch fixes that.

comment:2 Changed 6 years ago by julien

  • Has patch set

Sorry, I meant: MultiValueDict.update() is not prepared...

comment:3 follow-up: Changed 6 years ago by SmileyChris

  • Patch needs improvement set

From a quick scan of your patch:

  1. Your tests don't actually check for the regression of updating a QueryDict with a normal dict (which leads to...)
  1. It looks like this will break for updating normal dicts
  1. It'd be better to use QueryDict('...', mutable=True) rather than messing around with internal variables in the test

comment:4 in reply to: ↑ 3 Changed 6 years ago by julien

Replying to SmileyChris:

From a quick scan of your patch:

  1. Your tests don't actually check for the regression of updating a QueryDict with a normal dict (which leads to...)
  1. It looks like this will break for updating normal dicts
  1. It'd be better to use QueryDict('...', mutable=True) rather than messing around with internal variables in the test

I agree, I realised the patch was wrong shortly after I posted. Trying to give it another go.

comment:5 Changed 6 years ago by julien

Ok, I've amended the path and now all tests pass. The trick I used was to iterate through the list of values and updated value by value. Again, I'm not sure if that's the best approach, but at least it seems to work.

Changed 6 years ago by julien

2nd attempt. All tests pass.

comment:6 Changed 6 years ago by mtredinnick

  • Triage Stage changed from Unreviewed to Accepted

comment:7 Changed 6 years ago by jacob

  • Resolution set to fixed
  • Status changed from new to closed

(In [8705]) Fixed #8278: fixed QueryDict.update(QueryDict). Thanks, julien.

comment:8 Changed 3 years ago by jacob

  • milestone 1.0 deleted

Milestone 1.0 deleted

Add Comment

Modify Ticket

Change Properties
<Author field>
Action
as closed
as The resolution will be set. Next status will be 'closed'
The resolution will be deleted. Next status will be 'new'
Author


E-mail address and user name can be saved in the Preferences.

 
Note: See TracTickets for help on using tickets.