Pass (optional) parameters to HTTP parameter (Python, requests) - python

I am currently working on an API Wrapper, and I have an issue with passing the parameters from a function, into the payload of requests. The parameters can be blockId, senderId, recipientId, limit, offset, orderBy. All parameters join by "OR". One possible solution could be having if statements for every combination, but I imagine that that is a terrible way to do it. (requests and constants are already imported)
def transactionsList(*args **kwargs):
if blockId not None:
payload = {'blockId': blockId}
if offset not None:
payload = {'offset': offset}
...
r = requests.get(constants.TRANSACTIONS_LIST, params=payload, timeout=constants.TIMEOUT)
return r
What is (or are) more elegant ways to achieve parameters of the function getting passed to the requests payload?

Shortest one:
PARAMS = ['blockid', 'senderid', 'recipientid', 'limit', 'offset', 'orderby']
payload = {name: eval(name) for name in PARAMS if eval(name) is not None}

After tinkering around with Pythonist answer (which didn't work because there was always a NameError), I have come up with this solution:
def transactionsList(*args, **kwargs):
payload = {name: kwargs[name] for name in kwargs if kwargs[name] is not None}
r = requests.get(constants.TRANSACTIONS_LIST, params=payload, timeout=constants.TIMEOUT)
# print(r.url)
return r
As you can see, the important part is the payload:
payload = {name: kwargs[name] for name in kwargs if kwargs[name] is not None}
As long as there is a parameter (name) in the kwargs array and if it's value isn't None, it'll be added to the payload.

Related

get parameters in a get request in django rest framework?

I want to get the parameters sent to my rest api
what I want is to obtain the parameters that to use them consume another api and return the response of the third party api
but in name and comic i get None
http://127.0.0.1:8000/searchComics/
{name:"3-D Man","comics":12}
this is my view
class MarvelApi(APIView):
def get(self, request):
private_key = "88958f2d87bd2c0c2fa07b7ea654bcdf9f0389b3"
public_key = "8d415ffcc9add56b0a47c0a7c851afc3"
ts = 1
md5_hash = "46ecbbd63108b0561b8778a57823bd34"
query_params = self.request.query_params
name = query_params.get('kword', None)
comic = query_params.get('comic', None)
end_point = f"https://gateway.marvel.com:443/v1/public/characters?ts={ts}&apikey={public_key}&hash={md5_hash}&name={name}&comic={comic}"
response = requests.get(end_point)
response_json = json.loads(response.text)
return Response(status=status.HTTP_200_OK, data=response_json)
I think the problem is these two lines
name = query_params.get('kword', None)
comic = query_params.get('comic', None)
that do not capture the values ​​correctly, do you know how to solve it?
You wanted to get them from GET method, but instead you gave a dictionary, so I guess you sent it via POST. Instead of posting dictionary you should go with url:
http://127.0.0.1:8000/searchComics/?name=3-D+Man&comic=12
And you had probably a typo. You had plural "comics" in dictionary and you seek for "comic" singular.
And if you want to have data with POST method, just change def get(...) to def post(...).

How to save the header api at global

I have 2 functions which taking header of same. Can i save at global so that i no need to call every time
def funct1():
json_d = {"group_id": "uid"}
headers = {"account-id":"xxx","api-key":"xxx","Content-Type": "application/json"}
response = requests.post("https://example.com/docs",headers=headers,json=json_d)
def funct2():
json_d = {"group_id": "uid"}
headers = {"account-id":"xxx","api-key":"xxx","Content-Type": "application/json"}
response = requests.post("https://example.com/docs",headers=headers,json=json_d)
Can I do
headers = {"account-id":"xxx","api-key":"xxx","Content-Type": "application/json"}
global headers
i'd recommend making a function for a default http call.
In this case only the json_d is different per function. Maybe this will also be the URL or other things, you can easy move them up to the initial functions and parameterize them in the default_post() function.
Using **kwargs you can make it more generic, for example if you want to pass a timeout you can call default_post(..., timeout=3) which is passed automatically to the requests.post function.
Example
def funct1():
json_d_1 = {
"group_id": "uid_1"
}
default_post(json_d_1)
def funct2():
json_d_2 = {
"group_id": "uid_2"
}
default_post(json_d_2)
def default_post(json_d, ..., **kwargs):
headers = {"account-id":"xxx","api-key":"xxx","Content-Type": "application/json"}
response = requests.post(
"https://example.com/docs",
headers=headers,
json=json_d,
**kwargs
)
return response
I would suggest to create a function that will return the headers dictionary in order to "protect" it from changes:
def get_headers():
return {"account-id":"xxx","api-key":"xxx","Content-Type": "application/json"}
In this case at every call to the get_headers function new dictionary will be created so if you change it (the headers dictionary) in one function it does not effect the other function (unless what is that you are trying to achieve).

Setting default to NULL with format() returns unexpected string

In my API call defined below to retrieve the last 24 hrs of data, the normal request url would be:
https://api.foobar.com/data
That is why I have set the next_page parameter default to NULL.
However, sometimes the API will return a unique URL at the end of the json (such as https://api.foobar.com/data?page%237hfaj39), which indicates another page exists and another get_data request needs to be made to retrieve the remainder.
In that case, the {next_page} parameter will be set to whatever this unique url returned would be.
My problem is after adding the {next_page} parameter, the default get_data url somehow gets 4 unwanted characters - %7B%7D appended so that the request looks like
https://api.foobar.com/data%7B%7D and of course the API does not respond.
In UTF-8 encoding %7B%7D are two brackets {}
Why does this happen and what am I doing wrong here in terms of formatting? Using None in place of {} also does not work.
The code:
def make_request(url, params={}, headers={}):
r = requests.get(url, params=params, headers=headers)
print r.url
if(not r.status_code is 200):
print "Error access API" + r.text
exit()
return r.json()
def get_data(access_token, next_page={}):
end_time = int(round(time.time() * 1000))
start_time = end_time - (seconds_in_day * 1000)
headers = {'Authorization': 'Bearer ' + access_token, 'start_time': str(start_time), 'end_time': str(end_time)}
url = 'https://api.foobar.com/data{next_page}'.format(next_page=next_page)
return make_request(url, headers=headers)
Note: the API call works when the next_page parameter is removed
With next_page={}, you will get unexpected formatting results. If you try the following:
>>> '{}'.format({})
'{}'
As you can see, instead of the desired '', you get a string with two brackets. This is because:
>>> str({})
'{}'
A similar thing happens with None:
>>> '{}'.format(None)
'None'
>>> str(None)
'None'
To fix this, instead of next_page={}, try next_page='', because .format() will do this:
>>> '{}'.format('')
''

Why does this Python method re-use this variable? [duplicate]

This question already has answers here:
"Least Astonishment" and the Mutable Default Argument
(33 answers)
Closed 9 years ago.
I am writing a wrapper for a Rest API I interact with day to day. When you make a request for something which has many results, it paginates them. I was trying to write a function to elegantly de-paginate data and ran into something unexpected -- if I try to re-use this function in the same application or IPython session, it will stack the results of the second request on top of the results of the first request. Here is the code:
class RestAPIWrapper(object):
def __init__(self, url=url, acct=acct, token=token):
self.url = url
self._session = requests.Session()
self._session.mount('https://', HTTPAdapter(max_retries=5))
self._session.headers.update({'Accept': 'application/json', 'Content-Type': 'application/json'})
self._session.auth = (acct, token)
def search_api_broken(self, query, page=1, results=[]):
r = self._session.get('{0}/search.json?page={1}&query={2}'.format(self.url, page, query))
response = r.json()
results.extend(response['results'])
#returns a dictionary that has these keys: ['results', 'next_page', 'previous_page']
if response['next_page'] is not None:
results = self.search_api_broken(query, page=page+1, results=results)
return results
def search_api_works(self, query, page=1, results=[]):
if page == 1:
results = []
r = self._session.get('{0}/search.json?page={1}&query={2}&sort_by={3}&sort_order={4}'.format(self.base_url, page, quote(query), sort_by, sort_order))
response = r.json()
results.extend(response['results'])
#returns a dictionary that has these keys: ['results', 'next_page', 'previous_page']
if response['next_page'] is not None:
results = self.search_api_wroks(query, page=page+1, results=results)
return results
In other words, if I call the method like this:
my_api_wrapper = RestAPIWrapper()
#query should return 320 results, #query2 should return 140 results
data = my_api_wrapper.search_api_broken(query)
len(data)
#outputs 320
more_data = my_api_wrapper.search_api_broken(query2)
len(more_data)
#outputs 460
The output on the second method includes the first. Why does it do this since I put results = [] in the function definition? I'm not specifying it when I call the method, so it should default to an empty list, right?
in "search_api_broken", previous content in "results" is not clear.

A better pattern for ajax loading with pyramid?

I've read up on using different renderers or overriding renderer but I'm wondering if there's a better way to handle this pattern.
Right now, in my view I'm returning a set of items to the template:
#view_config(
route_name = 'name',
permission = 'perm',
renderer = 'r.mako'
)
def r( request ):
items = get_items()
return { 'items': items }
Now I want the ajax version to just renderer a subset of it, also with some data. My current working code:
#view_config(
route_name = 'name',
permission = 'perm',
renderer = 'r.mako'
)
def r( request ):
items = get_items()
if ajax:
return Response( to_json( {
'data1': 1,
'data2': 2,
'data3': 3,
'html': renderers.render( 'shortr.mako',
{ 'items': items },
request )
} )
return { 'items': items }
I guess specifically I wonder if there's a cleaner way to override the renderer and then wrap it in something, without explicitly calling render and making sure I got the dict right or request as a param. thanks
I would suggest using 2 views which properly allow you to apply a different "look-and-feel" (responses) to the same data.
def get_items(request):
return {} # values that you can pick and choose from in each view
#view_config(route_name='name', permission='perm', xhr=True, renderer='json')
def r_ajax(request):
items = get_items(request)
return {
'data1': 1, 'data2': 2, 'data3': 3,
'html': renderers.render('shortr.mako', {'items': items}, request),
}
#view_config(route_name='name', permission='perm', renderer='r.mako')
def r_html(request):
items = get_items(request)
return items
If you're afraid of repeating things for the view configuration, Pyramid 1.3 comes with a cool new feature on its class-based views:
#view_defaults(route_name='name', permission='perm')
class R(object):
def __init__(self, request):
self.request = request
self.items = # ...
#view_config(xhr=True, renderer='json')
def ajax(request):
return {
'data1': 1, 'data2': 2, 'data3': 3,
'html': renderers.render('shortr.mako', {'items': items}, request),
}
#view_config(renderer='r.mako')
def html(request):
return self.items
I'm not familiar with pyramid or what 'r.mako' is referencing, but you could probably hook in to the request before the controller is called with a custom function that inspects the Accepts headers of the request, looking for 'text/javascript' or 'application/json' as the foremost Accepts, and then set a flag on the request object (or have that method factored out for use in your r function).
Then do a custom renderer to handle either parsing with mako or dumping a json string
# pseudo-code
def before_controller(request, response):
if 'text/html' in request.headers.accepts:
request.is_json = False
elif 'application/json' in request.headers.accepts:
response.headers.set('Content-type', 'application/json')
request.is_json = True
# pseudo-code
def my_renderer(request, response, result):
if 'text/html' in request.headers.accepts:
return # render html
elif 'application/json' in request.headers.accepts:
response.headers.set('Content-type', 'application/json')
return json.dumps(result)
#
def r(request):
items = get_items()
if request.json:
pass # update items with additional data
return {'items': items} # one point of return
The method would also mean no extra leg-work if you dont need to do any additional processing on items, you simply return the result as normal and receive the json object on the other end.
If you cant hook into pyramid before the controller is called, you could write a convenience function to call is_json(request) and use that in the controller and the renderer for determining output and setting content-type header

Categories