I have an API which I want to consume in Python 3.x using the requests
module.
Right now, my solution is to create Requests objects for each URL like the LoginRequest
, etc. (see below).
I have one request per API URL and need to write the __init__
method for each class depending on the parameters that are requested for the API. I find it tedious and I am not sure that's a good design.
Could you suggest a better design? (e.g. not using OOP, with metaclass, ... ?)
class BaseRequest:
USER_AGENT = 'My App 1.0'
headers = {'User-Agent': USER_AGENT}
url = None
data = None
params = None
def __init__(self, url, data=None, headers=None, params=None):
self.url = url
if data:
data.update(self.data or {})
self.data = data
if headers:
headers.update(self.headers or {})
self.headers = headers
if params:
params.update(self.params or {})
self.params = params
def process(self):
r = requests.post(self.url, data=self.data, headers=self.headers, params=self.params)
try:
j = r.json()
except:
print(self.url, self.params, self.data)
print(r.text)
exit()
return j
class AuthenticatedRequest(BaseRequest):
API_KEY = 'blah'
APP_ID = 'OfficialClient'
data = {'appkey': API_KEY, 'appid': APP_ID}
class LoginRequest(AuthenticatedRequest):
url = 'https://my.api.com/login'
def __init__(self, email, password):
super().__init__(self.url, data=dict(email=email, password=password, action='login'))
# now let's play with the API !
user = LoginRequest(email, password).process()
GetUserInfoDetailsRequest(...).process()
1 Answer 1
It makes sense to model the API like this if things are more complicated, e.g. ability to pass around requests and responses as objects might come in handy.
The last example shows why this alone isn't that user-friendly though:
In addition to mentioning the specific request (with the Request
suffix always around) you also have to call the process
method.
I'd suggest an additional wrapper to make things easier in the normal
case, i.e. have login
function or method that does this for you.
The code as-is looks good to me with the exception of the process
method - firstly you can just write return r.json()
, secondly the
exit
call in there is ... questionable. I most certainly wouldn't
want to have a library call that on my program ever.
A minor thing I've also noticed is that the subclasses of BaseRequest
can't override any values via the constructor. I'm guessing that's
deliberate, I just usually expect to be able to do that (so in the base
class call self.headers.update(headers)
instead of the other way
round).