How to make put request with nested dictionaries to flask-restful? - python-3.x

The documentation example for a simple restful api is:
from flask import Flask, request
from flask_restful import Resource, Api
app = Flask(__name__)
api = Api(app)
todos = {}
class TodoSimple(Resource):
def get(self, todo_id):
return {todo_id: todos[todo_id]}
def put(self, todo_id):
todos[todo_id] = request.form['data']
return {todo_id: todos[todo_id]}
api.add_resource(TodoSimple, '/<string:todo_id>')
if __name__ == '__main__':
app.run(host="0.0.0.0",port="80",debug=True)
However, suppose I made a put request with a nested dictionary,ie {'data':{'fruit':'orange'}}. The TodoSimple would have request.form.to_dict() = {'data':'fruit'}. How can I work with the full nested dictionary?

You should probably use Schemas to achieve this goal. Take a good look at this first example of marshmallow docs:
https://marshmallow.readthedocs.io/en/3.0/
As flask-restful docs says:
The whole request parser part of Flask-RESTful is slated for removal
and will be replaced by documentation on how to integrate with other
packages that do the input/output stuff better (such as marshmallow).

Related

Adding query parameter for every flask GET request

Trying to figure out the right mechanism to use here.
I want to modify the flask request coming in every time.
I think the request is immutable, so I am trying to figure out if this mechanism exists.
Basically, I want to append a string onto the end of the request coming in.
I can hook into the request and the right time in a before_request handler like this:
#app.before_app_request
def before_request_custom():
# Get the request
req = flask.request
method = str(req.method)
if method == "GET":
# Do stuff here
pass
But I am not sure what to actually do to add this in, and don't see a way to accomplish it...I guess i could redirect, but that seems silly in this case. Any ideas?
The request object is immutable (https://werkzeug.palletsprojects.com/en/1.0.x/wrappers/#base-wrappers), but request.args or request.form can be set from ImmutableOrderedMultiDict to just OrderedMultiDict using Subclassing on Flask (https://flask.palletsprojects.com/en/1.1.x/patterns/subclassing/). Here's an example of how you could add that filter[is_deleted]=False URL param:
from flask import Flask, request, Request
from werkzeug.datastructures import OrderedMultiDict
class MyRequest(Request):
parameter_storage_class = OrderedMultiDict
class MyApp(Flask):
def __init__(self, import_name):
super(MyApp, self).__init__(import_name)
self.before_request(self.my_before_method)
def my_before_method(self):
if "endpoint" in request.base_url:
request.args["filter[is_deleted]"] = "False"
app = MyApp(__name__)
app.request_class = MyRequest
#app.route('/endpoint/')
def endpoint():
filter = request.args.get('filter[is_deleted]')
return filter
This way you can modify request.args before you actually send the request.
How about this?
from flask import g
#app.before_request
def before_request():
# Get the request
req = flask.request
method = str(req.method)
if method == "GET":
g.my_addon = "secret sauce"
return None
Then, g.my_addon is available in every view function:
from flask import g
#app.route('/my_view')
def my_view():
if g.my_addon == "secret sauce":
print('it worked!')
Using test_request_context() you can make the trick.
Related: https://flask.palletsprojects.com/en/1.1.x/quickstart/#accessing-request-data

Call Method of another class in Flask API

I am trying to expose a data service as an API for a PHP application. I have written the API logic in a file called APILogic.py. The code in this looks like this
class APILogic(object):
def __init__(self):
# the initialization is done here
...
def APIfunction(self):
# the API logic is built here, it uses the class variables and methods
...
I have created another file for the API purpose. Its called API.py. The code in this file looks like this
import APILogic from APILogic
class MyFlask:
def __init__(self):
self.test = APILogic()
from flask import Flask
app = Flask(__name__)
my_flask = MyFlask()
#app.route("/Test")
def Test():
return my_flask.test.APIfunction
if __name__ == "__main__":
app.run(debug=True,port=9999)
When I run the code, I get the error
> TypeError: APIfunction() takes 1 positional argument but 3 were given
The view function did not return a valid response. The return type must be a string, dict, tuple, Response instance, or WSGI callable, but it was a method.
There are no arguments for the APIfunction though.
Please help.
The view function did not return a valid response. The return type must be a string, dict, tuple, Response instance, or WSGI callable, but it was a method.
Looks like you're returning the method, but it sounds like you want to return the result of that method:
#app.route("/Test")
def Test():
return my_flask.test.APIfunction()
View function should return valid response.
Sample API code
from flask import Flask
app = Flask(__name__)
#app.route("/")
def hello():
return "Hello World!"
if __name__ == "__main__":
app.run()

Attribute error when testing Flask with PyTest fixture

Can someone explain how I could use the pytest fixtures in my tests?
I have been given this conftest.py where it defines 3 pytest fixtures:
{...}
#pytest.fixture(scope='session', autouse=True)
def app(request):
app = create_app({
'TESTING': True
})
ctx = app.app_context()
ctx.push()
def teardown():
ctx.pop()
request.addfinalizer(teardown)
return app
#pytest.fixture(scope='function')
def client(app, request):
return app.test_client()
#pytest.fixture(scope='function')
def get(client):
return humanize_werkzeug_client(client.get)
I am trying to test my app using the above test fixtures. Based on my undersanding, I need to use that app fixture in my pytests. As seen in this blog, I have tried something like this:
def test_myflaskapp(app):
response = app.get('/')
assert response.status_code == 200
But I get an attribute error: AttributeError: 'Flask' object has no attribute 'get'. This answer did not make any sense to me I am afraid not sure if it even applies on my case.
Can someone explain how/what I am doing wrong? I am trying to learn Flask/PyTest and I cannot find an example/guide that explains how this works.
The app fixture returns a Flask instance, I think you want to use .get method on the test client instance. Try changing app to client in your test.
Check out the official docs on testing.

Pass filepath as parameter to a URL in FLASK(Python)

I want to build an api which accepts a parameter from the user which is a filepath and then process the file given in that path. The file to be processed is already in the server where the api will be running.
As of now, I have written an api where I have hardcoded the filepath in my code which runs the api. Now, I want to configure my api in such a way that accepts a filepath from the user. My api should accept the path as a parameter and process the file that has been given in the path.
The api code is as follows:
The convert function returns the category of the file.
import ectd
from ectd import convert
from flask import Flask, request
from flask_restful import Resource, Api
#from flask.views import MethodView
app = Flask(__name__)
api = Api(app)
#convert(r'D:\files\67cecf40-71cf-4fc4-82e1-696ca41a9fba.pdf')
class ectdtext(Resource):
def get(self, result):
return {'data': ectd.convert(result)}
#api.add_resource(ectdtext, '/ectd/<result>')
categories=convert(r'D:\files\6628cb99-a400-4821-8b13-aa4744bd1286.pdf')
#app.route('/')
def returnResult():
return categories
if __name__ == '__main__':
app.run(host="0.0.0.0", port=5000)
So, I want to make changes to this code to accept a parameter from the user which will be a filepath and the convert function will process that filepath. I want to know how to make my api accept a filepath parameter from the user.
Trial with requests.args.get:
import ectd
from ectd import convert
from flask import Flask, request
from flask_restful import Resource, Api
#from flask.views import MethodView
app = Flask(__name__)
api = Api(app)
#convert(r'D:\files\67cecf40-71cf-4fc4-82e1-696ca41a9fba.pdf')
class ectdtext(Resource):
def get(self, result):
return {'data': ectd.convert(result)}
#api.add_resource(ectdtext, '/ectd/<result>')
#app.route('/')
def returnResult():
categories=convert(r'D:\files\'.format(request.args.get('categories')))
return categories
if __name__ == '__main__':
app.run(host="0.0.0.0", port=5000)
results in error :
"RuntimeError: Working outside of request context.
This typically means that you attempted to use functionality that needed
an active HTTP request. Consult the documentation on testing for
information about how to avoid this problem."
PRESENT SCENARIO:
I am able to post a filepath to the url. My question is now how do I use this posted url with filepath in my code to trigger my function that takes in the filepath and processes the file. Code to post the filepath:
import ectd
from ectd import convert
from flask import Flask, request
from flask_restful import Resource, Api
#from flask.views import MethodView
app = Flask(__name__)
api = Api(app)
class ectdtext(Resource):
def get(self, result):
return {'data': ectd.convert(result)}
#api.add_resource(ectdtext, '/ectd/<result>')
categories=convert('/home/brian/ajay/files/5ca21af9-5b67-45f8-969c-ae571431c665.pdf')
#app.route('/')
def returnResult():
return categories
#app.route('/', defaults={'path': ''})
#app.route('/<path:path>')
def get_dir(path):
return path
##app.route('/get_dir/<path>')
#def get_dir(path):
# return path
if __name__ == '__main__':
app.run(host="0.0.0.0", port=5000)

Python: Request handler in Flask

I'm learning Flask, and the request handling seems to be like:
#app.route("/")
def hello():
return "Hello World!"
So I end up defining the functions for all my routes in a single file. I'd much rather have functions for a model in its own file, e.g. get_user, create_user in user.py. I've used Express (node.js) in the past, and I can do:
user = require('./models/user')
app.get('/user', user.list)
where user.coffee (or .js) has a list function.
How do I do the same in Flask?
From the docs:
A decorator that is used to register a view function for a given URL rule. This does the same thing as add_url_rule() but is intended for decorator usage
The add_url_rule docs elaborate:
#app.route('/')
def index():
pass
Is equivalent to the following:
def index():
pass
app.add_url_rule('/', 'index', index)
You can just as easily import your view functions into a urls.py file and call add_url_rule once for each view function there instead of defining the rules along side the functions or use the lazy loading pattern.

Resources