I have written below code endpoint to post data received from front end, I have implemented the same way for get and it works but does not work for post
parser = reqparse.RequestParser()
parser.add_argument("update", type=bool, help="Update the data set")
parser.add_argument(
"set_name", type=str, help="Name of the set you want to add
#api.route("/add_set")
class AddNewSet(Resource):
#api.doc(parser=parser)
def post(self):
""" endpoint that handles request for deploying services
:return: (int) deployed service id from database.
"""
print "Hello hellooo"
args = parser.parse_args()
print "doink"
throws error:
{ "message": "Failed to decode JSON object: No JSON object could be decoded"
}
And the text "doink" does not get printed so I gave doubt parser.parse_args() is not working as expected I believe or I am doing something wrong.
By default, the RequestParser looks for args from request.json and request.values
Plz see: https://flask-restplus.readthedocs.io/en/stable/parsing.html#argument-locations
As for your code, I haven't try your way to define args for post, I do this way
#api.expect(api.model(
'ModelName', dict(
arg1 = fields.String(required=True),
arg2 = fields.Integer,
)
))
def post(self):
return {'result': 'success'}
I also recommend to give a clear return clause, even return None.
Related
I am learning Python-Flask and found out that there are two ways to create endpoints in an application.
1. app.routing(/endpoint)
2. api.add_resource(CLASSNAME, endpoint)
Using app.routing() we can just add an endpoint over a method and invoke it. Using api.add_resource() we need to register the class name & the end points.
I've seen that the method names are given as get() & put() if you are using api.add_resource()
For ex:
app = Flask(__name__)
api = Api(app)
vehicles = []
class VehicleData(Resource):
parser = reqparse.RequestParser()
parser.add_argument('vehicle', type=str, required=True, help='name cannot be empty')
parser.add_argument('type', type=str, required=True, help='vehicle type cannot be empty')
parser.add_argument('wheels', type=int, required=True, help='number of wheels cannot be empty')
parser.add_argument('suv', type=bool, required=False, help='SUV or not can be empty')
def get(self, name):
vehicle = next(filter(lambda x: x['name'] == name, vehicles), None)
return {'vehicle': vehicle}, 200 if vehicle else 404
def post(self, name):
# data = request.get_json()
# sport.append({'sportname': data['sport_name'], 'team_size':data['team_size'], 'popularity':data['popularity']})
if next(filter(lambda x: x['name'] == name, vehicles), None) is not None:
print("in the IF BLOCK")
return {'message': 'The vehicle {n} already exists in the database'.format(n=name)}, 404
v_data = VehicleData.parser.parse_args()
vehicle = {'name': name, 'type':v_data['type'], 'vehicle': v_data['vehicle'], 'suv': v_data['suv'], 'wheels': v_data['wheels']}
vehicles.append(vehicle)
return vehicle, 201
def getallvehicles(self):
return {'vehicles': vehicles}
api.add_resource(VehicleData, '/addvehicle/<string:name>', '/getvehicle/<string:name>')
app.run(port=5000, debug=True)
If I submit the API http://127.0.0.1:5000/getvehicle with a GET http call, I am properly getting the data as per the logic given in the code. In the code I have only one GET & POST method. So Flask is automatically invoking the get() when I submit http://127.0.0.1:5000/getvehicle/<name> with a GET request.
What if I have a method name other than get()/post()/put() ?
For example, if I have a method getallvehicles() that returns all the vehicles i.e returns the list vehicles, how can I register it in api.register() ?
For example, I tried this:
api.add_resource(VehicleData, '/addvehicle/<string:name>', '/getvehicle/<string:name>', '/getallvehicles')
and submitted the API call 'http://127.0.0.1:5000/getallvehicles` with a GET request, I am facing an error:
File "/Users/bobby/PyCharmProjects/FlaskAPI/venv/lib/python3.7/site-packages/flask_restful/__init__.py", line 583, in dispatch_request
resp = meth(*args, **kwargs)
TypeError: get() missing 1 required positional argument: 'name'
Could anyone tell me what is the mistake I did here and how can I give an endpoint for getallvehicles() in the below line and map it to a GET http request:
api.add_resource(VehicleData, '/addvehicle/<string:name>', '/getvehicle/<string:name>', '/getallvehicles')
The difference between the 2 different ways is, that:
This one is "native" flask method, that you can use to wrap your functions with.
#app.routing('/endpoint')
This one is a part of the restfull_flask package and it does the things in a different way than the native flask way.
api.add_resource(CLASSNAME, endpoint)
You can do the same stuff with both ways, but if you use the rest_framework, then you should use the second method :)
For the rest of your question I am sure you will find answers in this documentation:
https://flask-restful.readthedocs.io/en/latest/
I just started to code in Flask swagger backend. When I tried to extract multiple paths and the argument parameters, an error occurs, says:
TypeError: get() missing 1 required positional argument: 'q'
But actually, my "get()" function has the positional argument 'q'. I may ask why this error occurs? That could be weird since I use
request.arg.get('q')
it works good and can returns the expected query argument.
The expected API paths is:
GET 127.0.0.1:8888/book/1?q=question20
and my code is here:
from flask import Flask, request
from flask_restplus import Resource, Api
app = Flask(__name__)
api = Api(app)
#api.route("/<string:resource>/<int:rid>")
#api.doc(params={'q': 'Your query here'})
class API(Resource):
def get(self, resource, rid, q):
return {"resource": resource,
"id": rid,
"query": q
}, 200
if __name__ == "__main__":
app.run(host='127.0.0.1', port=8888, debug=True)
I would appreciate if someone can help me solve this problem.
You can use api.parser() to document and capture query parameters
parser = api.parser()
parser.add_argument('q', type=str, help='Resource name', location='args')
#api.route("/<string:resource>/<int:rid>")
#api.doc(parser=parser)
class API(Resource):
def get(self, resource, rid):
args = parser.parse_args()
q = args['q']
return {"resource": resource,
"id": rid,
"q":q,
}, 200
This method is marked as deprecated https://flask-restplus.readthedocs.io/en/stable/parsing.html
EDIT
This question is no longer relevant, since flask-restful new release can handle it by itself.
ORIGINAL QUESTION:
I have a flask-restful API, and I use reqparse in order to parse arguments.
Now, I want to abort the request if the user uses an unknown argument.
Using reqparse, I can abort when I detect a known argument with a bad value, but it doesn't seem to have a default case where I could treat the others.
It would prevent users to contact me with "why isn't it working ?" when they are the one who are not using correct arguments.
How would you do?
EDIT: As asked, here is an example of view:
class myView(restful.Resource):
def get(self):
parser = reqparse.RequestParser()
parser.add_argument('arg1', type=str, action='append')
parser.add_argument('arg2', type=myType, action='append')
args = parser.parse_args()
result = dao.getResult(arg1, arg2)
return jsonify(result)
api.add_resource(myView, '/view')
What I want is this: If a user goes to ip/view?bad_arg=bad then they get a 400 error.
Beginning with 0.3.1, you can pass strict=True to reqparse.parse_args. https://github.com/flask-restful/flask-restful/pull/358
reqparse does not provide a built in solution, but I was able to get over my problem by sub classing the reqparse parser.
class StrictParser(reqparse.RequestParser):
def parse_args(self, req=None):
"""Overide reqparse parser in order to fail on unknown argument
"""
if req is None:
req = request
known_args = [arg.name for arg in self.args]
for arg in request.args:
if arg not in known_args:
bad_arg = reqparse.Argument(arg)
bad_arg.handle_validation_error("Unknown argument: %s" % arg)
return reqparse.RequestParser.parse_args(self,req)
I have a problem making the simple (non-json) arguments work with POST. Just taking the simple example from their tutorials, I can't make a unit test where the task is passing as an argument. However task is never passed in. Its none.
class TodoList(Resource):
def __init__(self):
self.reqparse = reqparse.RequestParser()
self.reqparse.add_argument('task', type = str)
super(TodoList, self).__init__()
def post(self):
args = self.reqparse.parse_args()
#args['task'] is None, but why?
return TODOS[args['task']], 201
Unit test:
def test_task(self):
rv = self.app.post('todos', data='task=test')
self.check_content_type(rv.headers)
resp = json.loads(rv.data)
eq_(rv.status_code, 201)
What am I missing please?
When you use 'task=test' test_client do not set application/x-www-form-urlencoded content type, because you put string to input stream. So flask can't detect form and read data from form and reqparse will return None for any values for this case.
To fix it you must set up content type or use dict {'task': 'test'} or tuple.
Also for request testing better to use client = self.app.test_client() instead app = self.app.test_client(), if you use FlaskTesting.TestCase class, then just call self.client.post.
I'm just beginning to learn python/django. I've been teaching myself PHP and now I would like to learn python. I'm having issues integrating yelp's API. I'm getting errors:
Values instance has no attribute 'q'
I have this code:
def search(request):
parser = optparse.OptionParser()
parser.add_option('-c', '--consumer_key', dest='my_consumer_key_goes_here', help='OAuth consumer key (REQUIRED)')
parser.add_option('-s', '--consumer_secret', dest='my_consumer_secret_goes_here', help='OAuth consumer secret (REQUIRED)')
parser.add_option('-t', '--token', dest='my_token_goes_here', help='OAuth token (REQUIRED)')
parser.add_option('-e', '--token_secret', dest='my_token_secret_goes_here', help='OAuth token secret (REQUIRED)')
parser.add_option('-a', '--host', dest='host', help='Host', default='api.yelp.com')
options, args = parser.parse_args()
# search stuff?
if 'q' in request.GET and request.GET['q']:
q = request.GET['q']
parser.add_option('-q', '--term', dest=q, help='Search term')
url_params = {}
if options.q:
url_params['term'] = options.q
# Sign the URL
consumer = oauth2.Consumer(consumer_key, consumer_secret)
oauth_request = oauth2.Request('GET', url, {})
oauth_request.update({'oauth_nonce': oauth2.generate_nonce(),
'oauth_timestamp': oauth2.generate_timestamp(),
'oauth_token': token,
'oauth_consumer_key': consumer_key})
token = oauth2.Token(token, token_secret)
oauth_request.sign_request(oauth2.SignatureMethod_HMAC_SHA1(), consumer, token)
signed_url = oauth_request.to_url()
else:
message = 'You submitted an empty form.'
#return HttpResponse(message)
print 'Signed URL: %s\n' % (signed_url,)
# Connect
try:
conn = urllib2.urlopen(signed_url, None)
try:
response = json.loads(conn.read())
finally:
conn.close()
except urllib2.HTTPError, error:
response = json.loads(error.read())
return response
response = request(options.host, '/v2/search', url_params, options.consumer_key, options.consumer_secret, options.token, options.token_secret)
print json.dumps(response, sort_keys=True, indent=2)
Now as I'm still new to this whole python language, I wonder if I'm also adding the API consumer_key, secret, etc... in the right places? Do I even have this code set correctly? Most of it was from the original yelp api script. I guess the issue lies in the if options.q: area... not sure if I'm doing this correctly?
Thanks
I would say that calling parser.parse_args() before you declared your q option is probably your immediate problem.
But unless I am missing something about what you are trying to do, Django settings [1] are what you need to use for those first 5 options (don't use optparse at all). And then I don't know what you are trying to do with the rest of the function.
[1] https://docs.djangoproject.com/en/1.4/topics/settings/