python-requests: order get parameters
Asked Answered
A

5

22

I am implementing a client library for a private HTTP-API using python requests. The API(which I don't control) expects the parameters to be in a certain order, but python-requests doesn't honor a sorted dict as parameter.

This is what i tried:

import requests
from django.utils.datastructures import SortedDict

params = SortedDict()
params['s'] = 'value1'
params['f'] = 'value2'

requests.get('https://example.org/private_api', params=params)
#performs request as https://example.org/private_api?f=value1&s=value2 

This is what I am trying to avoid:

requests.get('https://example.org?{0}'.format(urlencode(params)))
Amelioration answered 10/1, 2012 at 12:1 Comment(4)
Why do you use data param instead of params when doing get request?Phenocryst
@Piotr Dobrogost: That was an error when I compiled the example.Amelioration
Why is this an issue? GET parameters are usually accessed by name not by order.Salters
Because I don't control the API and that's the way they expect it.Amelioration
P
14

Currently requests doesn't allow to do this as you wish. This is of course shortcoming that will be fixed. However as params parameter can take not only dictionary but bytes as well you should be able to do something in between:

from collections import OrderedDict
from urllib import urlencode
import requests

params = OrderedDict([('first', 1), ('second', 2), ('third', 3)])
requests.get('https://example.org/private_api', params=urlencode(params))

This doesn't work as I see due to bug in line 85 of models.py: self.params = dict(params or []. I raised this problem in issue Wrong handling of params given as bytes object

Phenocryst answered 10/1, 2012 at 12:34 Comment(3)
Thanks, I finally marked this as the solution as it provides the solution that is applicable in the foreseeable future.Amelioration
@TillBackhaus Already fixed.Phenocryst
What version does this work for? When I tried it in both 1.2.3 and 2.0.0, requests.get('http://example.org/private_api', params=urlencode(params)).request.body returned NoneFictitious
C
28

The requests lib now supports this out-of-the-box: To get ordered parameters you use a sequence of two-valued tuples instead. This eliminates the additional requirement of OrderedDict.

payload = (('key1', 'value1'), ('key2', 'value2'))
r = requests.get("http://httpbin.org/get", params=payload)

Demo:

>>> import requests
>>> requests.__version__
1.2.3
>>> payload = (('key1', 'value1'), ('key2', 'value2'), ('key3', 'value3'))
>>> r = requests.get("http://httpbin.org/get", params=payload)
>>> print r.json()['url']
http://httpbin.org/get?key1=value1&key2=value2&key3=value3
Cedilla answered 28/8, 2013 at 14:23 Comment(0)
P
14

Currently requests doesn't allow to do this as you wish. This is of course shortcoming that will be fixed. However as params parameter can take not only dictionary but bytes as well you should be able to do something in between:

from collections import OrderedDict
from urllib import urlencode
import requests

params = OrderedDict([('first', 1), ('second', 2), ('third', 3)])
requests.get('https://example.org/private_api', params=urlencode(params))

This doesn't work as I see due to bug in line 85 of models.py: self.params = dict(params or []. I raised this problem in issue Wrong handling of params given as bytes object

Phenocryst answered 10/1, 2012 at 12:34 Comment(3)
Thanks, I finally marked this as the solution as it provides the solution that is applicable in the foreseeable future.Amelioration
@TillBackhaus Already fixed.Phenocryst
What version does this work for? When I tried it in both 1.2.3 and 2.0.0, requests.get('http://example.org/private_api', params=urlencode(params)).request.body returned NoneFictitious
V
2

It used with version 2.2.0:

import requests
yourparams = {'s' : 'value1', 'f': 'value2'}

test = requests.get('https://example.org/private_api', params=yourparams)
print(test.url)

More details? Kindly check in here.

Variometer answered 2/2, 2014 at 11:13 Comment(1)
This does not preserve order of params which was requested by OP.Phenocryst
S
1

Line 85 of requests/models.py (link) turns the params object into a plain dict, rather than the SortedDict you passed in. I don't think you will be able to do what you want, unless you patch the library.

self.params = dict(params or [])
Salters answered 10/1, 2012 at 12:35 Comment(1)
Considering the options at I think I'll have to go with what I was trying to avoid then. Thank you.Amelioration
F
0

I found that this works in 1.2.3 as well as 2.0.0

>>> import requests
>>> requests.__version__
'2.0.0'
>>> data = [('first', 1), ('second', 2), ('third', 3)]
>>> requests.get('http://example.org/private_api', data=data).request.body
'first=1&second=2&third=3'
Fictitious answered 26/9, 2013 at 8:39 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.