I'm a newbie in Flask and was wondering how to implement Flask API (http://www.flaskapi.org/) along with the main website, for example, main website (/), access API (/api/). In fact, this gives me an Internal Server Error.
Thank you.
from flask import Flask, request, url_for
from flask.ext.api import FlaskAPI, status, exceptions
# the all-important app variable:
app = Flask(__name__)
app2 = FlaskAPI(__name__)
#app.route("/")
def hello():
return "Oh, Hello Worldaa"
notes = {
0: 'do the shopping',
1: 'build the codez',
2: 'paint the door',
}
def note_repr(key):
return {
'url': request.host_url.rstrip('/api/') + url_for('notes_detail', key=key),
'text': notes[key]
}
#app2.route("/api/", methods=['GET', 'POST'])
def notes_list():
"""
List or create notes.
"""
if request.method == 'POST':
note = str(request.data.get('text', ''))
idx = max(notes.keys()) + 1
notes[idx] = note
return note_repr(idx), status.HTTP_201_CREATED
# request.method == 'GET'
return [note_repr(idx) for idx in sorted(notes.keys())]
#app2.route("/api/<int:key>/", methods=['GET', 'PUT', 'DELETE'])
def notes_detail(key):
"""
Retrieve, update or delete note instances.
"""
if request.method == 'PUT':
note = str(request.data.get('text', ''))
notes[key] = note
return note_repr(key)
elif request.method == 'DELETE':
notes.pop(key, None)
return '', status.HTTP_204_NO_CONTENT
# request.method == 'GET'
if key not in notes:
raise exceptions.NotFound()
return note_repr(key)
if __name__ == "__main__":
app.run(debug=True)
app2.run(debug=True)
You can look at Blueprint in the documentation of Flask.
For example:
main = Blueprint(__name__)
#main.route("/")
def index():
return "Hello, World."
api = Blurprint(__name__)
#api.route("/test")
def test():
return jsonify(dict(test="test")})
app = Flask(__name__)
app.register_blueprint(main)
app.register_blueprint(api, url_prefix="/api")
and then, you access index page by http://example.com, access api by http://example.com/api/test
Related
Context: I have two Flask routes which process the same request data (one interactively, one as an API). To keep my code DRY, I want to write a function process_post_request() that
accepts the request as in input parameter from each route,
parses the request,
returns results which the routes can use.
For example, in views.py:
#app.route('/interactive', methods=['GET', 'POST'])
def interactive():
if request.method == 'POST':
sum, product = process_post_request(request)
# present the results on a web page
#app.route('/api', methods=['GET', 'POST'])
def api():
if request.method == 'POST':
sum, product = process_post_request(request)
# return the results so that JavaScript can parse them
def process_post_request(request):
param1 = request.form.get('param1')
param2 = request.form.get('param2')
sum = param1 + param2
product = param1 * param2
return sum, product
Question: How can I write a pytest for process_post_request()? The problem is that if I create a "request" and try to pass it to process_post_request(), the request goes to a route, so that route returns a result. For example, in views_test.py:
import pytest
#pytest.fixture
def client():
"""Create a client to make requests from"""
with app.test_client() as client:
with app.app_context():
pass
yield client
def test_process_post_request():
request = client.post('/interactive', data={'param1': 5, 'param2': 7})
sum, product = process_post_request(request)
assert sum == 12
Because request returns a response, pytest throws this error:
> request = client.post('/interactive', data={'param1': 5, 'param2': 7})
E AttributeError: 'function' object has no attribute 'post'
I created an app route to return comma-separated parameters:
#app.route('/pass-through', methods = ['GET', 'POST'])
def pass_through():
if request.method == 'POST':
params = process_post_request(request)
return ','.join(params)
Then tested that app route:
def test_process_post_request():
with app.test_client() as client:
response = client.post('/pass-through', data={'param1': 5, 'param2': 7})
sum, product = response.data.decode().split(',')
assert sum == 12
where decode() translates from bytes to string.
It's not the most satisfying solution because it really tests the app route, so it depends on the app route function pass_through() using the same parameter names as process_post_request().
I am trying to run my flask app, but every time I load my index page, it gives me the error:
AttributeError: 'Flask' object has no attribute 'login_manager'.
It works before I put in this specific code
bp = flask.Blueprint("bp", __name__, template_folder="./build")
#bp.route('/index')
#login_required
def index():
# TODO: insert the data fetched by your app main page here as a JSON
DATA = {"your": "data here"}
data = json.dumps(DATA)
return flask.render_template(
"index.html",
data=data,
)
app.register_blueprint(bp)
This is my current code where it does work
#app.route("/index", methods=["GET", "POST"])
def index():
global current_user
if not current_user:
return flask.redirect(flask.url_for("login"))
if flask.request.method == "GET":
track_name, genius_link, track_artist, track_image, track_url = render()
# If user has no favorite artists, redirect back to profile.
if track_name == None:
return flask.redirect(flask.url_for("profile"))
return flask.render_template(
"index.html",
variable=track_name,
variable1=genius_link,
variable2=track_artist,
variable3=track_image,
variable4=track_url,
)
else:
valid_artist = validate_and_insert_artist(flask.request.form["artistId"])
if not valid_artist:
return flask.render_template("index.html", error=True)
else:
track_name, genius_link, track_artist, track_image, track_url = render()
# If user has no favorite artists, redirect back to profile.
if track_name == None:
return flask.redirect(flask.url_for("profile"))
return flask.render_template(
"index.html",
variable=track_name,
variable1=genius_link,
variable2=track_artist,
variable3=track_image,
variable4=track_url,
)
I am not sure why as soon as I put in the blueprint code, it stops working and gives me that error
This is my login.html
#app.route("/login", methods=["GET", "POST"])
def login():
global current_user
if current_user:
return flask.redirect(flask.url_for("profile"))
if flask.request.method == "GET":
return flask.render_template("login.html")
if flask.request.method == "POST":
username = flask.request.form["username"]
cursor.execute(
"SELECT user_name FROM public.users WHERE user_name = %s", [username]
)
results = cursor.fetchall()
if len(results) != 0: # if a user exists, "log" them in
current_user = username
return flask.redirect(flask.url_for("profile"))
else:
return flask.render_template("login.html", error=True)
You need to read the Flask documentation for #login_required. As soon as you've added a method that requires the user to be logged in, you need to provide a method by which the user can log in.
Or perhaps you just want to delete the #login_required?
My code for the flask server is this,
from flask import Flask, request
app = Flask(__name__)
todos = {}
#app.route('/<todo_id>', methods=['GET', 'PUT', 'DELETE'])
def main(todo_id):
if request.method == 'GET':
try:
return {todo_id: todos[todo_id]}
except:
return {"status": "paused or off"}
elif request.method == 'DELETE':
if todo_id in todos:
del todos[todo_id]
return '', 204
else:
return '', 404
elif request.method == 'PUT':
json = {"modules": request.form['modules'],
"hits":request.form['hits'],
"customs":request.form['customs'],
"free":request.form['free'],
"bads":request.form['bads'],
"checked":request.form['checked'],
"remaining":request.form['remaining'],
"retries":request.form['retries'],
"errors":request.form['errors'],
"cpm":request.form['cpm']
}
print(1)
todos[todo_id] = json
print(1)
return {todo_id: todos[todo_id]}
if __name__ == '__main__':
app.run(host="0.0.0.0", port=8080, debug=True)
And my client code is like this,
import requests
params = (
('cpm', '123'),
("modules", "doordash|COD"),
("hits", '63242'),
("customs", "888"),
("free", "233"),
("bads", "3892"),
("checked", '20000'),
("remaining", '200000'),
("retries", "1000"),
("errors", '1234')
)
response = requests.put('https://animeaskedforitt.my name.repl.co/123?modules=edigubv', params=params)
print(response)
r = requests.get("https://animeaskedforitt.myname.repl.co/123").text
print(r)
Every time I run it I get a 500 error from the put request and a 200 succes code.
In the console I get a 400 error saying “bad request” and a python error of KeyError: module
Thanks for your help
so I get this error
"Could not build URL for endpoint 'success'. Did you forget to specify values ['name']?"
what is do be done to rectify this
This is the main snippet if the server.py
from flask import Flask, redirect, url_for, request, render_template
app = Flask(__name__)
#app.route('/success/<name>')
def success(name):
return 'welcome %s' % name
#app.route('/',methods = ['POST', 'GET'])
def login():
if request.method == 'POST':
user = request.form['nm']
return redirect(url_for('success',name = user))
else:
user = request.args.get('nm')
return redirect(url_for('success',name = user))
return render_template('login.html')
if __name__ == '__main__':
app.run(debug = True)
login.html that contains a simple form with the
<html>
<body>
<form action = "http://localhost:5000/login" method = "post">
<p>Enter Name:</p>
<p><input type = "text" name = "nm" /></p>
<p><input type = "submit" value = "submit" /></p>
</form>
</body>
</html>
Your /login route is not mapped to any handler.
Rather login() is attached to / route which when accessed (GET request) doesn't contain nm in request.args and therefore your user variable is set to None.
from flask import Flask, redirect, url_for, request, render_template
app = Flask(__name__)
#app.route('/success/<name>')
def success(name):
return 'welcome %s' % name
#app.route('/login',methods = ['POST', 'GET'])
def login():
if request.method == 'POST':
user = request.form['nm']
return redirect(url_for('success',name = user))
else:
user = request.args.get('nm')
return redirect(url_for('success',name = user))
#app.route('/')
def index():
# Assuming template is available
return render_template('login.html')
if __name__ == '__main__':
app.run(debug = True)
I'm new to Flask and wondering if it is possible to use the same URL to display a form in html and query to display something
Ideally, I want to have the following result to happen.
If I'm going to create a query inside 138.10.2.1/sample by doing this:
http://138.10.2.1:8000/sample?psi=1&lavr=1&dsc=1&ifsc=1&ics=1&eng=3&ol1=1&ol2=1&reso=1&educ=1&listen=1&time=1&probe=1&unders=1
It will display:
*something in json format*
in the webpage
Else, if I'll just go straight to this:
http://138.10.2.1:8000/sample
It will direct me to a .html page with a form to fill or allow a user to attach a file to use and upload to display a transformed file in json format also.
Here's my code
sample.py
from flask import Flask, flash, request, redirect, url_for, make_response, send_from_directory, render_template
import convert as ps
app = Flask(__name__)
#app.route("/sample", methods=["GET", "POST"])
def query_strings():
#This is not working: if request.method == "POST":
args1 = request.args["psi"]
args2 = request.args["lavr"]
args3 = request.args["dsc"]
args4 = request.args["ifsc"]
args5 = request.args["ics"]
args6 = request.args["eng"]
args7 = request.args["ol1"]
args8 = request.args["ol2"]
args9 = request.args["reso"]
args10 = request.args["educ"]
args11 = request.args["listen"]
args12 = request.args["time"]
args13 = request.args["probe"]
args14 = request.args["unders"]
args_list = [args1, args2, args3, args4, args5, args6, args7, args8,args9, args10, args11, args12, args13, args14]
result = ps.execute(args_list)
response = app.response_class(
response=result,
status=200,
mimetype='application/json'
)
return response
#This is my html form: return render_template("form.html")
if __name__ == '__main__':
app.run(debug = True)
Right now, what I can have is to run the query but I am getting prompted to enter the paramaters I declared if I just entered:
http://138.10.2.1:8000/sample
You can check the number of arguments and return the HTML form if the length is 0 like this:
# if there are no arguments provided, show HTML form
if len(request.args) == 0:
return render_template("form.html")
Also, there's no need to store each argument as a separate variable and then combine them into a list. request.args is already a dictionary so you can simply get a list of the argument names and values with:
list(request.args.keys()) # list of argument names eg. ['psi', 'lavr', 'dsc', 'ifsc'...]
list(request.args.values()) # list of argument values eg. [1, 1, 1, 1...]
You can check if the argument names match a desired set:
if set(argument_names) == {"psi","lavr","dsc","ifsc","ics","eng","ol1","ol2","reso","educ","listen","time","probe","unders"}:
# return JSON
Overall, your code could look something like this:
from flask import Flask, request, render_template
import json
app = Flask(__name__)
#app.route("/sample", methods=["GET", "POST"])
def query_strings():
# if there are no arguments provided, show HTML form
if len(request.args) == 0:
return render_template("form.html")
argument_names = list(request.args.keys())
# if the argument list is valid
if set(argument_names) == {"psi","lavr","dsc","ifsc","ics","eng","ol1","ol2","reso","educ","listen","time","probe","unders"}:
# return JSON
response = app.response_class(
response=json.dumps(request.args),
status=200,
mimetype='application/json'
)
return response
return "Invalid arguments"
if __name__ == '__main__':
app.run(debug = True)
This will:
show form.html if you load /sample with no arguments
show the arguments as JSON if you load /sample with valid arguments (eg. /sample?psi=1&lavr=1&dsc=1&ifsc=1&ics=1&eng=3&ol1=1&ol2=1&reso=1&educ=1&listen=1&time=1&probe=1&unders=1)
show "Invalid arguments" in any other case
Ohh..I see..so how can I have a .html to display if I don't enter any parameters? This will allow me to attach a file inside the html form and create to POST and get the same json result
Based on your comment to the question and sample code you've posted I assume you might be looking for something like this:
#app.route("/sample", methods=["GET", "POST"])
def query_strings():
args1 = request.args.get("psi")
args2 = request.args.get("lavr")
args3 = request.args.get("dsc")
args4 = request.args.get("ifsc")
args5 = request.args.get("ics")
args6 = request.args.get("eng")
args7 = request.args.get("ol1")
args8 = request.args.get("ol2")
args9 = request.args.get("reso")
args10 = request.args.get("educ")
args11 = request.args.get("listen")
args12 = request.args.get("time")
args13 = request.args.get("probe")
args14 = request.args.get("unders")
args_list = [
args1, args2, args3, args4, args5, args6, args7, args8,
args9, args10, args11, args12, args13, args14
]
if not all(args_list):
return render_template('form.html')
else:
result = ps.execute(args_list)
response = app.response_class(
response=result,
status=200,
mimetype='application/json'
)
return response
In this case, if you give no parameters in GET request it will render a template with html form.
Also rather than always check for request method I suggest you take a look at Flask's MethodView. Using that you can nicely split you logic onto request with arguments in the query string and form submitting with json:
http://flask.pocoo.org/docs/1.0/api/#flask.views.MethodView
Try this code, I hope it will help you. By default, it uses the GET method, so it is not working. When you click on the submit button then it calls the POST method.
from flask import Flask, flash, request, redirect, url_for, make_response, send_from_directory, render_template
import convert as ps
app = Flask(__name__)
#app.route("/sample", methods=["GET", "POST"])
def query_strings():
if request.method == "POST":
args1 = request.args["psi"]
args2 = request.args["lavr"]
args3 = request.args["dsc"]
args4 = request.args["ifsc"]
args5 = request.args["ics"]
args6 = request.args["eng"]
args7 = request.args["ol1"]
args8 = request.args["ol2"]
args9 = request.args["reso"]
args10 = request.args["educ"]
args11 = request.args["listen"]
args12 = request.args["time"]
args13 = request.args["probe"]
args14 = request.args["unders"]
args_list = [args1, args2, args3, args4, args5, args6, args7, args8,args9, args10, args11, args12, args13, args14]
result = ps.execute(args_list)
response = app.response_class(
response=result,
status=200,
mimetype='application/json'
)
# return response
return render_template("form.html", response = response)
return render_template("form.html")
#This is my html form: return render_template("form.html")
if __name__ == '__main__':
app.run(debug = True)