Django Views: When is request.data a dict vs a QueryDict?

I have run into some trouble with the issue, that request.data sometimes is a dict (especially when testing) and sometimes a QueryDict instance (when using curl).

This is especially a problem because apparently there is a big difference when calling a view using curl like so:

curl -X POST --data "some_float=1.23456789012123123" "" 

Or using the django_webtest client like so:

class APIViewTest(WebTest): def test_testsomething(self): self.app.post(url=url, params=json.dumps({some_float=1.26356756467})) 

And then casting that QueryDict to a dict like so

new_dict = dict(**request.data) my_float = float(new_dict['some_float']) 

Everything works fine in the tests, as there request.data is a dict, but in production the view crashes because new_dict['some_float'] is actually a list with one element, and not as expected a float.

I have considered fixing the issue like so:

 if type(request.data) is dict: new_dict = dict(**request.data) else: new_dict = dict(**request.data.dict()) 

which feels very wrong as the tests would only test line 2, and (some? all?) production code would run line 4.

So while I am wondering why QueryDict behaves in this way, I would rather know why and when response.data is a QueryDict in the first place. And how I can use django tests to simulate this behavior. Having different conditions for production and testing systems is always troublesome and sometimes unavoidable, but in this case I feel like it could be fixed. Or is this a specific issue related to django_webtest?

4

2 Answers

Your test isn't a reflection of your actual curl call.

In your test, you post JSON, which is then available as a dict from request.data. But your curl call posts standard form data, which is available as a QueryDict. This behaviour is managed by the parsers attribute of your view or the DEFAULT_PARSER_CLASSES settings - and further note that this is functionality specifically provided by django-rest-framework, not Django itself.

Really you should test the same thing as you are doing; either send JSON from curl or get your test to post form-data.

5

When your request content_type is "application/x-www-form-urlencoded", request.Data become QueryDict.

see FormParser class.

And

QueryDict has get lists method. but it can't fetch dict value.
convert name str to array.

<input name="items[name]" value="Example"> <input name="items[count]" value="5"> 

And define custom form paser.

class CustomFormParser(FormParser): """ Parser for form data. """ media_type = 'application/x-www-form-urlencoded' def parse(self, stream, media_type=None, parser_context=None): """ Parses the incoming bytestream as a URL encoded form, and returns the resulting QueryDict. """ parser_context = parser_context or {} encoding = parser_context.get('encoding', settings.DEFAULT_CHARSET) data = QueryDict(stream.read(), encoding=encoding) return parse_json_form(data.dict()) # return dict 

And overwite DEFAULT_PARSER_CLASSES.

0

Your Answer

Sign up or log in

Sign up using Google Sign up using Facebook Sign up using Email and Password

Post as a guest

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

You Might Also Like