I am learning Flask and I am facing this issue - python

I am learning Flask. I wrote the basic code and I want the submitted text to display in the same page. I already wrote the html and connected it. How can I do this?
from flask import Flask, redirect, url_for,render_template, request
app = Flask(name)
#app.route("/", methods=["POST", "GET"])
def home():
if request.method == "POST":
user = request.form["nm"]
return redirect(url_for("/", user))
else:
return render_template("login.html")
if name == ("main"):
app.run(debug=True)

I've noticed that you've taken the code from Python Basics. Indeed they do not show how to format the HTML template of the redirect.
Luckily, they offer a tutorial that shows you how to feed retrieved data to an HTML template using Jinja2. This tutorial can be found here. In essence, you can use {{ variable }} in your HTML template. In Flask, you will have to specify the variable as argument in the render_template function.
Minimal example:
# app.py
#app.route('/result',methods = ['POST', 'GET'])
def result():
if request.method == 'POST':
variable = request.form['variable']
return render_template("result.html", variable=variable)
<!-- result.html -->
<p> This is your variable: {{ variable }} </p>
I advice you to also check out both the Flask and Jinja2 documentation, as they offer plenty comprehensive examples of how to work with callbacks and HTML templating.

Related

Flask render_template not returning the correct file

I have been coding a flask app that works perfectly fine while running locally, but while hosting the app a strange bug occurs. After I've posted a string to the server it responds with the expected page, but when I repeat it with another request (without restarting the server) the same page as earlier is displayed yet again.
It seems like the render_template does not update the changes I've done in the HTML file after it already has rendered the template, even though the command is run again.
#app.route("/", methods=['POST', 'GET'])
def index():
if request.method == 'POST':
content = str(request.form['content'])
# Some code that changes the "output.html" page based on the user input
return render_template('output.html')
else:
return render_template("index.html")
Why not use redirect instead of render_template in your if condition, like this:
#app.route("/", methods=['POST', 'GET'])
def index():
if request.method == 'POST':
content = str(request.form['content'])
return redirect(url_for('output.html'))
else:
return render_template("index.html")
I found the solution. Apparently the page doesn't update if it has the same URL as before even though a new template is rendered under the same URL. The solution was to make a dynamic URL.

Update webpage with cURL and flask

I have started learning flask for web development and experimenting with it since I have a website in mind I would like to create.
My thought has been to create a homepage showing some data that will be updated using cURL or the requests python library. I have some other python code that generates the data to be displayed and I would like to use the POST request for sending a dictionary with the generated information to the server and update the homepage with that new info.
A rather simplistic but comprehensive version of what I have tried so far:
from flask import Flask, redirect, url_for, render_template, request
app = Flask(__name__)
#app.route("/", methods=["POST", "GET"])
def home():
if request.method == "POST":
data = request.form["data"]
return render_template("index.html", content=data)
else:
return render_template("index.html", content="initial_data")
if __name__ == "__main__":
app.run(debug=True)
The code for index.html
<!doctype html>
<html>
<head>
<title>Home Page</title>
</head>
<body>
{{content}}
</body>
</html>
So I would like to send a POST request with some new data to the server and update the webpage with that new data.
import requests
payload = {'data': 'new_data'}
r = requests.post("http://localhost:5000/", data=payload)
All of the above doesn't succeed in updating the data in the webpage when I send a request, it just stays the same. Am I totally off course? Is this even possible in the way I have thought of?
Any comment is much appreciated, thank you.
data is staying the same because it's being reassigned each time as a local variable. When a POST request happens, the variable is created, passed to the template, then destroyed as it goes out of scope.
Try this as an experiment in showing how a variable might persist between page loads, though only until the server restarts:
from flask import Flask, redirect, url_for, render_template, request
app = Flask(__name__)
app_state = {
'data': 'Initial Data'
}
#app.route("/", methods=["POST", "GET"])
def home():
if request.method == "POST":
app_state['data'] = request.form['data']
return render_template("index.html", content=app_state['data'])
if __name__ == "__main__":
app.run(debug=True)
In that example, you're creating a global dictionary, app_date, and updating a key on it every time the user sends a POST request to / with a form payload of data.
Then, we return the template with the content of the dictionary item.
A Few Notes
The implementation I provided would still be vulnerable to race conditions if two people make simultaneous changes.
For this kind of thing, you'll usually want a more persistent solution, like a database.

Template is not shown on the server

I am very new to Flask and Web development so sorry in advanced if I use incorrect terms.
I am trying to create a webpage using Python and Flask. To do so, I have the following:
from flask import Flask, request, render_template, redirect, url_for
from flask_assets import Environment, Bundle
app = Flask(__name__)
assets = Environment(app)
#app.route("/", methods=["GET", "POST"])
def login():
# Code to do the login
error = None
if request.method == 'POST':
# code checking the passwords. if correct:
return render_template('index.html')
# else:
# error
return render_template('login.html', error = error)
What this snippet of code does is to load the login.html where the user is asked to put an username and password and after checking if they are the ones expected, it loads index.html where the user can upload his or her data. Once the data is submitted a new function is called:
#app.route('/transform', methods=["POST"])
def transform():
f = request.files['data_file']
if not f:
return "No file"
# code
return render_template('message.html')
The problem is that while in local the message.html gets display once transform has finished, in the server it doesn't appear although the function eventually does what it's expected to do. The other two templates are correctly displayed both in local and in the server. Could it be due to be in a different route?
The index.html is defined with action='\transform', in case it may give a hint.
Any idea of why could this be happening?

pythonanywhere + flask: website just says 'unhandled exception'. How to get debugger to print stack trace?

Be forewarned of a triple-newbie threat - new to python, new to python anywhere, new to flask.
[pythonanywhere-root]/mysite/test01.py
# A very simple Flask Hello World app for you to get started with...
from flask import Flask
from flask import render_template # for templating
#from flask import request # for handling requests eg form post, etc
app = Flask(__name__)
app.debug = True #bshark: turn on debugging, hopefully?
#app.route('/')
#def hello_world():
# return 'Hello from Flask! wheee!!'
def buildOrg():
orgname = 'ACME Inc'
return render_template('index.html', orgname)
And then in [pythonanywhere-root]/templates/index.html
<!doctype html>
<head><title>Test01 App</title></head>
<body>
{% if orgname %}
<h1>Welcome to {{ orgname }} Projects!</h1>
{% else %}
<p>Aw, the orgname wasn't passed in successfully :-(</p>
{% endif %}
</body>
</html>
When I hit up the site, I get 'Unhandled Exception' :-(
How do I get the debugger to at least spit out where I should start looking for the problem?
The problem is render_template only expects one positional argument, and rest of the arguments are passed as keyword only arguments.So, you need to change your code to:
def buildOrg():
orgname = 'ACME Inc'
return render_template('index.html', name=orgname)
For the first part, you can find the error logs under the Web tab on pythonanywhere.com.
You need to also pass your name of orgname variable that is used in your template to render_template.
flask.render_template:
flask.render_template(template_name_or_list, **context)
Renders a template from the template folder with the given context.
Parameters:
template_name_or_list – the name of the template to be rendered,
or an iterable with template names the first one existing will be rendered
context – the variables that should be available in the context of the template.
So, change this line:
return render_template('index.html', orgname)
To:
return render_template('index.html', orgname=orgname)

Unable to use flask.g to access variables in other functions

I'm trying to use flask.g to store variables that can be accessed in other functions, but I don't seem to be doing something correctly. The application generates the following error when I try to access g.name: AttributeError: '_RequestGlobals' object has no attribute 'name'.
The documentation for flask.g says:
Just store on this whatever you want. For example a database
connection or the user that is currently logged in.
Here's a complete, minimal example that illustrates the error that I receive when trying to access the variable outside of the function it was created in. Any help would be greatly appreciated.
#!/usr/bin/env python
from flask import Flask, render_template_string, request, redirect, url_for, g
from wtforms import Form, TextField
application = app = Flask('wsgi')
#app.route('/', methods=['GET', 'POST'])
def index():
form = LoginForm(request.form)
if request.method == 'POST' and form.validate():
name = form.name.data
g.name = name
# Need to create an instance of a class and access that in another route
#g.api = CustomApi(name)
return redirect(url_for('get_posts'))
else:
return render_template_string(template_form, form=form)
#app.route('/posts', methods=['GET'])
def get_posts():
# Need to access the instance of CustomApi here
#api = g.api
name = g.name
return render_template_string(name_template, name=name)
class LoginForm(Form):
name = TextField('Name')
template_form = """
{% block content %}
<h1>Enter your name</h1>
<form method="POST" action="/">
<div>{{ form.name.label }} {{ form.name() }}</div><br>
<button type="submit" class="btn">Submit</button>
</form>
{% endblock %}
"""
name_template = """
{% block content %}
<div>"Hello {{ name }}"</div><br>
{% endblock %}
"""
if __name__ == '__main__':
app.run(debug=True)
The g object is a request-based object and does not persist between requests, i.e. g is recreated between your request to index and your request to get_posts.
Application Globals in Flask:
Flask provides you with a special object that ensures it is only valid for the active request and that will return different values for each request. In a nutshell: it does the right thing, like it does for request and session.
For persistent storage of tiny data between requests use sessions instead. You may (but should not) get away with storing the data in the app object directly for global (all sessions) application state, similar to what config does, if you find a really good reason to do so.
For more complex data use databases.
If you need to track authentication information, I'd suggest one of the Flask plugins like Flask-Login or Flask-Principal.
For example, we use Flask-Principal. It raises the identity-loaded signal when somebody authenticates (or it detects an authentication cookie). We then map their logged-in identity with a user in our database. Something like this:
# not actual code
#identity_loaded.connect_via(app)
def on_identity_loaded(sender, identity):
user = Person.query.filter(Person.username==identity.person.username).one()
g.user = user
and then we can use g.user in any controller or template. (We're actually ripping a lot of this out, it was a easy, lazy hack that's caused more trouble than it's worth.)
If you don't want to use a module, there's a built-in signal you can hook into at the start of every request:
http://flask.pocoo.org/docs/tutorial/dbcon/
# This runs before every request
#app.before_request
def before_request():
g.user = your_magic_user_function()
and g.user would then be magically available everywhere.
I hope that helps!
Just use sessions in flask. In your case, you just want to save the user/name in your request and the easiest way is to use sessions.
from flask import session
app.secret_key = 'some key for session'
Then, your functions could be changed as below:
#app.route('/', methods=['GET', 'POST'])
def index():
form = LoginForm(request.form)
if request.method == 'POST' and form.validate():
session['name'] = form.name.data
return redirect(url_for('get_posts'))
else:
return render_template_string(template_form, form=form)
#app.route('/posts', methods=['GET'])
def get_posts():
if 'name' in session:
name = session['name']
else:
name = "Unknown"
return render_template_string(name_template, name=name)
I will like to shed more light on the use of g global in storing data. g only store data with a request and when redirecting to another route, the g global is set back to null i.e it reset back to nothing. This means whatever set to g in one request can't be access in another request. Use sessions to store data that will be accessed across request.
One benefit of using g global is when connecting to a database to fetct a user. For example, may be the admin from the database. The admin can be store in the g global using the below method.
from flask import Flask, g
app = Flask(__name__)
#app.before_request
def text():
g.a = User.query.filter_by(email='admin#gmail.com')
#app.route("/getTrue", methods=['GET', 'POST'])
def getTrue():
form = UserForm()
if form.validate_on_submit():
if g.a == form.email.data:
return "Admin is logged in"
else:
return "User is logged in"
return render_template('login.html', form=form)
In the example above, the g can be use to save data which will be use in another request. I hope this help. Thanks

Categories