Changing an HTML file through Flask - python

I have an HTML form (in a very basic Flask site, adjusted to be inside a class), which takes in some values and will upload them to a database. What I'd like is to send the user some kind of a message on the same page after they click the 'submit' input button. That is - not to serve up a different HTML file, but to change, say one of the divs in my home.html file, or add a new div. At the moment I've thought of two possiblities:
A JS script, with onclick() to change the InnerHTML of one of the divs;
Serving up a new, almost identical HTML file, with one line changed to say something like "information received".
However, I have a feeling that there's a simpler way to do this, within the same file (home.html) using a method of Flask. I've spent a while searching through the docs and SO, without success.
class UI:
def __init__(self):
app = Flask(__name__)
#app.route('/')
def home():
return render_template("home.html")
#app.route('/', methods=['POST', 'GET'])
def update_info():
foo = request.form['foo']
bar = request.form['bar']
if request.method == 'POST':
return render_template("new_home.html")
app.debug = True
app.run()

A JS script, with onclick() to change the InnerHTML of one of the divs;
This would be the best option in my opinion. Create a div to contain the message above the form--or anywhere else you'd want it. I would suggest using jQuery for this as it's ajax method is very nice for this type of thing. Return either a success or failure response from your app(setting the status code, so jQuery knows what happened), and use the done promise to display success or the fail promise to display errors.

Related

Why does inserting a function inside a route differ from inserting the code inside the function in Flask?

I am trying to make a web app with a login system. I want to make it so that a user can't access certain pages unless they are logged in.
What I want is that when you click to go to another page while not logged in, you get redirected to the login page and on it you get a message flash.
This is what works:
#app.route("/home", methods=['GET', 'POST'])
def home():
#some form
if not current_user.is_authenticated:
flash('You need to be logged in to access this page.', 'info')
return redirect(url_for('login'))
#rest of the code
But I would need to add all of this to other routes as well. So I created the function and added it to the routes instead:
#app.route("/home", methods=['GET', 'POST'])
def home():
#some form
require_login()
#rest of the code
def require_login():
if not current_user.is_authenticated:
flash('You need to be logged in to access this page.', 'info')
return redirect(url_for('login'))
But this does not work as I want it to. It instead redirects to the home page and then flashes the message. How do I fix this?
The problem is that the redirect(...) doesn't itself do the redirect. It returns a value to Flask telling flask that it needs to do the redirect.
In your first piece of code, you handle this correctly. You take the result of redirect(...) and return it to flask. In your second piece of code, you take the redirection returned by require_login and ignore it in home.
You might try something like:
value = require_login()
if value:
return value
You need to return the function
return require_login()
But be aware, after that u cant have code. You should create an decorator for this. There are examples online just Google "flask authorized decorator"
Your Advantage of this that u can move the auth Logic out of the Views and you can easily decorate your Views and dont have this Stuff in each View/route

Flask: How to render a template AND save request.headers info?

I am trying to save objects made available by the request.headers in my Flask app.
I want to render my index.html upon page load, but I also want to grab the visiting user's email so I can use it for other functions / processes.
# routes
#app.route('/')
def index():
return render_template('index.html')
def find_aad():
aad_email = request.headers.get('X-MS-CLIENT-PRINCIPAL-NAME') # aad email
return aad_email
If I try to run find_aad() on its own,
user_email = find_aad() # cant run
I will get the typical error: Working outside of request context.
How can I on an initial load of the website secure these headers and save them to an object without having these errors?
You could get at it this way, perhaps:
On that first call to index, you can create a UUID for the "session" and use that as an identifier for the user, then you pass that code back inside the rendered UI elements for stashing on the client-side. Then, on every subsequent call to the backend, you send that UUID with the rest of the request.
On those subsequent requests, you can access the email value via that UUID as the key to the data structure you're using to store client information on the backend.
This concept is the idea of a "session" with a "session id" that is common in client/server communications. Using sockets or possibly even built in or supplemental libraries for Flask would probably be a good idea instead of "rolling your own". Sorry if I'm being unhelpful or stupid - it's late where I'm at.
EDIT:
By request here's some simple pseudocode for this:
from flask import Flask
import uuid
...
uuid_to_email = {}
...
#app.route('/')
def index():
user_id = str(uuid.uuid4())
uuid_to_email[user_id] = request.headers.get('X-MS-CLIENT-PRINCIPAL-NAME')
return render_template('index.html', uuid=user_id) # where it is implied that you would then use the uuid in the client-side code to story it and pass it back to the endpoints you want to do that with

How do I clear out user input after each use on pythonanywhere- trying to use Flask sessions

I am trying to build a simple site where a user inputs a paragraph of information (which is largely the same format each time), then I have a function that alters that paragraph to simplify it (the simplify_report function below), and spits the simplified version out for the user to have.
So far the program below works really well and I'm mostly happy with it. However, I am using pythonanywhere and the "comments" variable is global so it doesn't erase the simplified report when I reload the page (they just build up). I have looked into Flask's sessions option, but I just can't seem to figure out making it work with the below program (all the examples I'm seeing are for user login and I just need the site to be cleaned up/cleared out with each reload).
Alternatively, could I at least add a button the user could push to clear out the old outputs/comments? How would I go about that?
I appreciate any help!
#From the main file:
from flask import Flask, render_template, request, url_for, redirect
from run import simplify_report
app = Flask(__name__, template_folder="templates")
comments =[]
app.config["DEBUG"] = True
#app.route("/", methods=["GET", "POST"])
def index():
if request.method == "GET":
return render_template("page.html", comments=comments)
newreport = simplify_report(request.form["contents"])
comments.append(newreport)
return redirect(url_for('index'))
#Relevant section from my page.html file:
{% for comment in comments %}
<div class="row">
{{ comment }}
</div>
{% endfor %}
As a side note, my 'newreport' generated is all jumbled into one line and I can't seem to figure out how to format it to separate it into separate different lines. Not my main issue here though.
You need to store the data outside your web app on PythonAnywhere. Consider using a database for that. There is no persistency for data in the memory, as processes serving your web app could stop and start on a different server at any time.

Pass variable from jinja2 template to python

Sorry if this is a noob question I am still learning. I have passed a variable from python code to a jinja2 HTML template to set up a URL, like this:
Delete
When this link is pressed it should run a query that deletes the entity with that ID. But when the link is pressed it goes to /delete/1827424298 for example, which results in a 404 error as the request handler doesn't exist.
I need to pass that ID back into my python code so it can run a method to delete the entity with that same ID. How do I go about doing this? Using webapp2 if that is important.
class DeleteRequestHandler(webapp2.RequestHandler):
def get():
template = template_env.get_template('myrequests.html')
context = {
'results': results.key.id()
}
self.response.out.write(template.render(context))
EDIT: I've added my delete handler - it is incomplete as I have yet to add the query to delete the entity. My thinking behind it so far is I can grab the results.key.id() from the jinja2 template and put it into results but I am not sure if this would work.
So I think what you're confused about is how to set up a route handler with a dynamic part to the URL. It's a shame that this is completely skipped over in the webapp2 tutorial, as it's a fundamental part of writing any web application. However, it is covered well in the guide to routing, which you should read.
At its simplest, it's just a matter of putting a regex in the route:
app = webapp2.WSGIApplication([
...
(r'/delete/(\d+)', MyDeleteHandler),
])
which will now route any URL of the form /delete/<number>/ to your deletion handler.
The ID that you pass in the URL will be the first positional argument to the handler method:
class MyDeleteHandler:
def get(self, item_id):
key = ndb.Key(MyModel, item_id) # or whatever

UndefinedError : 'user' is undefined

I am currently developing a Flask app (have been for the past year) and I'm encountering a rather... Weird bug. I've got a few files that are always included in my Jinja2 templates (navbars), and they use the users' name and avatar. As a consequence, everytime I render a template, I pass it the user. I recently noticed an error on my prod server :
<img alt="image" class="img-circle" src="{{ user.image }}" style="width: 48px;"/>
File "/usr/local/lib/python2.7/dist-packages/jinja2/environment.py", line 397, in getattr
return getattr(obj, attribute)
jinja2.exceptions.UndefinedError: 'user' is undefined
This is in one of my navbars. The method that renders this template uses this :
#mod.route('/broken_pus', methods=['POST', 'GET'])
def view_broken_pus():
return render_template("view_broken_pus.html", user=g.user, urls_for_active_clients=DeletedURLs.objects()[0].urls_for_active_clients, other_urls=DeletedURLs.objects()[0].other_urls)
As you can see, I pass the user=g.user. I do this on every single view of my website. And it works everywhere, EXCEPT on this method, which is pretty small. I have plenty of other routes like that, with just a render template, so I don't get what's the problem.
I also get it on another method, bigger, which always worked before :
#mod.route('/users/add', methods=['GET', 'POST'])
#requires_roles("admin", "project-leader")
def add():
"""
Method adding a new user.
"""
# We do not use WTForms there since we need custom checkboxes for the role
# Instead we use basic HTML and treat the checkboxes here
if request.method == 'POST':
user = User(name=request.form.get('name'),
email=request.form.get('email'))
l = []
# big switch assignement
user.role = l
try:
user.save()
except errors.NotUniqueError:
flash(u'User %s already in database.' % user.name, 'danger')
return redirect(url_for('home'))
flash(u'User %s registered.' % user.name, 'success')
return redirect(url_for('home'))
return render_template('add_user.html', page=url_for('users.add'), user=g.user, clients=Client.objects())
When I first load the form for adding a user, it works. When I add it, for some reason, I get the error (and the user is not saved in the database).
Since this works perfectly on local, I'm starting to suspect a problem on the production server itself. We use nginx and uwsgi for the app, and I recently implemented some Celery tasks. Got any idea ?
Thanks in advance.
Check out flask source for render_template:
It just calls template.render(context), but after the call to before_render_template.send(app, template=template, context=context)
From this, I think there is some before_render_template handler, that modifies context installed.
To debug this down, I may try to call something like this:
from flask import app
#mod.route('/broken_pus', methods=['POST', 'GET'])
def view_broken_pus():
template = app.jinja_env.get_or_select_template("view_broken_pus.html")
return template.render(dict(
user=g.user,
urls_for_active_clients=DeletedURLs.objects()[0].urls_for_active_clients,
other_urls=DeletedURLs.objects()[0].other_urls,
))
If this will work, I will need to dig in who modifies context in before_render_template slot.
I suspect threading. If g is some sort of global reference then you may need to ensure that it is set up on threading.local or that threading locks are used to ensure that no thread can get hold of g.user before some 'other' thread messes with it.
See how do I make a 2.7 python context manager threadsafe for a way to handle 'globals' without sacrificing thread safety.

Categories