3

I have a python dict

x = {'id': 123, 'data': {'param1': 'hello', 'param2': 'world'}}

I'm trying to get my form-data to be

id=123
data[param1]=hello
data[param2]=world

I cannot pass it as a json, because the API receiving it won't accept json objects

I've tried passing it like this

requests​.post(url, data=x, headers={'content-type': 'application/x-www-form-urlencoded'})

The request goes out with form data looking like this

id=123
data=param1
data=param2

Is there a way to get around this?

Alon
  • 743
  • 10
  • 23

3 Answers3

0

Instead of posting data = x you should be posting data=x['data']. This should solve your issue.

requests​.post(url, data=x['data'], headers={'content-type': 'application/x-www-form-urlencoded'})

Or better way would be to remove the data key from your dict and directly create dict as

x = {'param1': 'hello', 'param2': 'world'}

And then you can post like:

requests​.post(url, data=x['data'], headers={'content-type': 'application/x-www-form-urlencoded'})

Choose what works best for you..

Hope this helps :)

coderadi
  • 35
  • 7
-1

You can flatten-out the first level:

x = {'data': {'param1': 'hello', 'param2': 'world'}}

x_flattened = {"{}[{}]".format(e, s) if type(: x[e][s] for e in x for s in x[e]}

requests​.post(url, data=x_flattened)

edit - you've added more info to your original object in the meantime. If you want to preserve the top level singular parameters as well, you can do it in two steps (you can do it in single dict comprehension but it gets ugly quickly):

x_flattened = {}
for e in x:
    if isinstance(x[e], (list, dict)):
        x_flattened.update({"{}[{}]".format(e, s): x[e][s] for s in x[e]})
    else:
        x_flattened[e] = x[e]

requests​.post(url, data=x_flattened)
zwer
  • 24,943
  • 3
  • 48
  • 66
  • That's... unwise. Data serialization is not as simple as a little bit of string interpolation. This will break if the data contains quotes or brackets/braces. On top of that it's extremely likely that the OP is actually looking for standard URL-encoded key/value pairs, just as the content type of the request suggests. – Tomalak Jun 08 '17 at 13:31
  • I didn't say it's wise... The `requests` module doesn't provide workarounds for imbecile APIs that require some special data packing for some unfathomable reason. This is the most basic way to achieve what OP wants so it gets him started... he'll have to handle the edge-cases, or find the writer of the API he's trying to communicate with and tell him to fix the expectation on the receiving side. – zwer Jun 08 '17 at 13:36
  • "special data packaging" and `application/x-www-form-urlencoded` are pretty much a contradiction in terms. There is little that's more un-special than URL encoding. Apart from that, "edge-case" is not a good defense for incorrect code. – Tomalak Jun 08 '17 at 13:44
  • `application/x-www-form-urlencoded` doesn't say anything on nested data which is what the OP wants to pack (so that Pythons `x["data"]["param1"] = "foo"` becomes `data[param1]=foo` when POSTed). The single-level packing part will be dealt by the `requests` module properly, but the OP will need to flatten out the nested levels if he wants to send the data like he presented. The code above is a step in that direction and will most likely work in the majority of cases. – zwer Jun 08 '17 at 13:53
  • Fair enough, but without a proper specification it's useless to do *something* and hand-wave the rest as "edge cases". If the OP wants to flatten out a nested structure then this is a recursive operation that needs some spelled-out rules and produces a flat dict, which then needs to go through proper URL encoding. – Tomalak Jun 08 '17 at 14:15
-1

Perhaps you can do it well. Let's try it

x = {
   'id': 123,
   'data[param1]': 'hello',
   'data[param2]': 'world',
}

https://github.com/requests/requests/issues/2885

tell k
  • 605
  • 2
  • 7
  • 18