How can I yield a template over another in flask? - python

I have been working on a project in flask and I am stuck on one part where I need to figure out how to yield one flask template over another.
To illustrate what I mean, for example, I have a program like this.
main.py
from flask import Flask, stream_with_context, Response, render_template
app = Flask('app')
#app.route('/')
def hello_world():
def generate():
yield render_template('index.html')
yield render_template('index2.html')
return Response(stream_with_context(generate()))
app.run(host='0.0.0.0', port=8080)
index.html
<h3>Hi</h3>
index2.html
<h3>Bye</h3>
Running main.py returns:
Hi
Bye
Even though this makes sense, my goal is to make it result in just Bye which should replace Hi. I tried other paths like returning both but none of them worked. Any ideas on how I can do this?

It's not your case, but if you'd like to stream a template with static content, here's a way to do it. I'll be using the sleep() method to suspend the execution for 1 second.
from flask import Flask, stream_with_context, request, Response, flash
import time
from time import sleep
app = Flask(__name__)
def stream_template(template_name, **context):
app.update_template_context(context)
t = app.jinja_env.get_template(template_name)
rv = t.stream(context)
rv.disable_buffering()
return rv
data = ['Hi', 'Bye']
def generate():
for item in data:
yield str(item)
sleep(1)
#app.route('/')
def stream_view():
rows = generate()
return Response(stream_with_context(stream_template('index.html', rows=rows)))
if __name__ == "__main__":
app.run()
where templates/index.html:
{% for item in rows %}
<h1>{{ item }}</h1>
{% endfor %}
See streaming from templates in the documentation.

You would have to do you function different to use a generator like this.
from flask import Flask, stream_with_context, Response, render_template
app = Flask('app')
def page_generator():
yield render_template('index.html')
yield render_template('index2.html')
generator_obj = None
#app.route('/')
def hello_world():
global generator_obj
generator_obj = generator_obj or page_generator()
return Response(stream_with_context(next(generator_obj)))
app.run(host='0.0.0.0', port=8080)
I don't know for sure if this will work in flask.
Note that after you call hello_world twice this will fail unless you reset generator_obj to None on StopIteration.

Related

How to access Flask Request header outside route method

I want to access headers for a certain API calls outside of its api route. I tried using the app_context and test_request_context but it doesn't seem to work.
from flask import Flask, request
app = Flask("app")
def access_header_for_something():
with app.test_request_context():
with app.app_context():
print(request.headers.get("Authorization"), request.host, request.path)
#app.route('/')
def index():
access_header_for_something()
return 'hello'
if __name__=="__main__":
app.run(debug=True)
Any suggestions would be really helpful
The above code snippet work with slight tweak:
from flask import Flask, request
app = Flask("app")
def access_header_for_something():
with app.app_context():
print(request.headers.get("Authorization"), request.host, request.path)
#app.route('/')
def index():
access_header_for_something()
return 'hello'
if __name__=="__main__":
app.run(debug=True)

Is there a way to pass HTML code in flask as string?

Is there a way to pass HTML code as a string in python/flask rather than render template HTML file in a directory?
for example
from flask import Flask, render_template
app = Flask(__name__)
#app.route('/')
def home():
return render_template('index.html') # Instead of giving file path I want to pass HTML code directly
if __name__ == '__main__':
app.run()
I guess you mean something like this:
from flask import Flask
app = Flask(__name__)
HTML_AS_TEXT = """<p>hello world</p>"""
#app.route('/')
def home():
return HTML_AS_TEXT
if __name__ == '__main__':
app.run()

url_for() parameter was ignored in flask

My route method is:
#app.route('/movie/')
def movie(page_num=1):
#...detail skipped...
And my template is:
<li>{{ page_num }}</li>
When I click the link, the address bar shows "127.0.0.1:5000/movie/?page_num=5",but the pagination.page shows it is still page 1.
Why the parameter was ignored and how can I fix it?
Since you skipped the code of your function it's hard to say what's wrong. But I suspect that you just don't catch GET parameters correctly. To do this you can either use variable rules with dynamic name component in your route; or access parameters submitted in the URL with request.args.get.
Here's a minimal example showing boths methods:
from flask import Flask, url_for, request
app = Flask(__name__)
#app.route('/')
def index():
link = url_for('movie',page_num=5)
return "<a href='{0}'>Click</a>".format(link)
#app.route('/index2')
def index_get():
link = url_for('movie_get',page_num=5)
return "<a href='{0}'>Click</a>".format(link)
#app.route('/movie/<page_num>')
def movie(page_num=1):
return str(page_num)
#app.route('/movie_get')
def movie_get():
param = request.args.get('page_num', '1')
return str(param)
if __name__ == '__main__':
app.run(debug=True)

Rewrite a URL with Flask

Is it possible to rewrite a URL with Flask e.g. if a POST request is received via this route:
#app.route('/', methods=['GET','POST'])
def main():
if request.method == 'POST':
#TODO: rewrite url to something like '/message'
return render_template('message.html')
else:
return render_template('index.html')
I'm aware I can use a redirect and setup another route but I prefer to simply modify the url if possible.
You can call your route endpoint functions from another routes:
# The “target” route:
#route('/foo')
def foo():
return render_template(...)
# The rewrited route:
#route('/bar')
def bar():
return foo()
You can even access g etc. This approach can be also used when the Flask's routing facilities cannot do something that you want to implement.
This is actual solution to this problem (but maybe a dirty hack):
def rewrite(url):
view_func, view_args = app.create_url_adapter(request).match(url)
return app.view_functions[view_func](**view_args)
We invoke the URL dispatcher and call the view function.
Usage:
#app.route('/bar')
def the_rewritten_one():
return rewrite('/foo')
I think you could be interested in redirect behavior
from flask import Flask,redirect
then use
redirect("http://www.example.com", code=302)
see: Redirecting to URL in Flask
from flask import Flask, redirect, url_for
app = Flask(__name__)
#app.route("/")
def home():
return "hello"
#app.route("/admin"):
def admin():
return redirect(url_for("#name of function for page you want to go to"))

Bad Request Error with flask, python, HTML, unusual initialization behavior with flask.request.form

I'm writing a web-app using flask, python and HTML. My issue is that the first time I load the a webpage, I get the following error
Bad Request The browser (or proxy) sent a request that this server
could not understand.
I'm able to get the page to load eventually by "tricking" first running it without any flask.request.form calls, and then putting them back in (details below). Something must be going wrong in my initialization. I'm new to flask and using python with HTML.
Assume I'm working from a directory called example. I have a python script called test.py and an HTML template called test.html with the following directory structure:
\example\test.py
\example\templates\test.html
My python script test.py is:
import sys
import flask, flask.views
app = flask.Flask(__name__)
app.secret_key = "bacon"
class View(flask.views.MethodView):
def get(self):
result = flask.request.form['result']
return flask.render_template('test.html', result=result)
# return flask.render_template('test.html')
def post(self):
return self.get()
app.add_url_rule('/', view_func=View.as_view('main'), methods=['GET', 'POST'])
app.debug = True
app.run()
and my HTML in test.html is
<html>
<head>
</head>
<body>
<form action="/" method="post">
Enter something into the box:
<input type="text" name="result"/><br>
<input type="submit" value="Execute!"/>
</form>
</body>
</html>
Steps to reproduce the error
1: Run the test.py script, and open up the URL in a browser
Running on http://127.0.0.1:5000/
You should see the following error
Bad Request The browser (or proxy) sent a request that this server
could not understand.
2: Comment out the first 2 lines of the def get(self) function and uncomment the 3rd line of the def get(self) function so that test.py looks like this
import sys
import flask, flask.views
app = flask.Flask(__name__)
app.secret_key = "bacon"
class View(flask.views.MethodView):
def get(self):
# result = flask.request.form['result']
# return flask.render_template('test.html', result=result)
return flask.render_template('test.html')
def post(self):
return self.get()
app.add_url_rule('/', view_func=View.as_view('main'), methods=['GET', 'POST'])
app.debug = True
app.run()
3: Refresh the URL, and you will see that things work (though I ultimately want to be able to return the value of result
4: Now, switch the lines that are commented out again. I.e, uncomment the first 2 lines of the def get(self) function and comment out the 3rd line of the def get(self) function so that test.py looks like this
import sys
import flask, flask.views
app = flask.Flask(__name__)
app.secret_key = "bacon"
class View(flask.views.MethodView):
def get(self):
result = flask.request.form['result']
return flask.render_template('test.html', result=result)
# return flask.render_template('test.html')
def post(self):
return self.get()
app.add_url_rule('/', view_func=View.as_view('main'), methods=['GET', 'POST'])
app.debug = True
app.run()
5: Refresh the URL and now you see things will be working as desired.
This is just a toy example illustrating the real problem exhibiting this weird behavior of how I have to "trick" my browser into showing me this webpage. The
The issue here is that you are attempting to access POSTed variables in a method that will only handle GET requests. When you attempt to access a query string or POST parameter that is not set Flask will, by default, raise a BadRequest error (because you are asking for something that the person hitting the page did not supply).
What happens if the key does not exist in the form attribute? In that case a special KeyError is raised. You can catch it like a standard KeyError but if you don’t do that, a HTTP 400 Bad Request error page is shown instead. So for many situations you don’t have to deal with that problem.
If you need to access a variable from either request.args (GET) or request.form (POST) and you don't need it to be set use the get method to get the value if it is there (or None if it is not set.
# Will default to None
your_var = request.form.get("some_key")
# Alternately:
your_var = request.form.get("some_key", "alternate_default_value")
Here's an alternate way of structuring your code:
import sys
import flask, flask.views
app = flask.Flask(__name__)
app.secret_key = "bacon"
app.debug = True
class View(flask.views.MethodView):
def get(self):
"""Enable user to provide us with input"""
return self._default_actions()
def post(self):
"""Map user input to our program's inputs - display errors if required"""
result = flask.request.form['result']
# Alternately, if `result` is not *required*
# result = flask.request.form.get("result")
return self._default_actions(result=result)
def _default_actions(self, result=None):
"""Deal with the meat of the matter, taking in whatever params we need
to get or process our information"""
if result is None:
return flask.render_template("test.html")
else:
return flask.render_template("test.html", result=result)
app.add_url_rule('/', view_func=View.as_view('main'), methods=['GET', 'POST'])
if __name__ == "__main__":
app.run()

Categories