In most simple cases I would make the following form in a html file like this:
<form action="/do_this" method ="POST">
and the action would carry out whatever that function did within the app.py file.
I'm now deep within a blueprint and I just want to set that form action in a simple way like the above but I can't figure out how to reference a certain .py file and then a function within it.
Any pointers?
You should use url_for().
In this case you would do:
<form action="{{ url_for('your_blueprint.do_this') }}" method="POST">
The step which is of interest for me in the blueprint setup is the final line in this example:
from flask import Flask
from yourapplication.your_blueprint import your_blueprint
app = Flask(__name__)
app.register_blueprint(your_blueprint) # hook your blueprint to your app
where Flask is instructed where to find your blueprint's functions.
Not relevant to this particular question, but something I just now learned is that if you're in the blueprint to which the action will go you can prefix the URL with . as a shortcut, e.g.:
<form action="{{ url_for('.do_this') }}" method="POST">
Related
I am having bigger Flask app and I want to utilize blueprints for cleaner design. Current code looks like this:
main_app.py:
app = Flask(__name__)
# ... some config passing to app ...
app.register_blueprint(user_management, url_prefix="/user")
and user_management.py
user_management = Blueprint("user_management", __name__, static_folder="static", template_folder="templates")
#user_management.route("/register")
def register_user():
register_form = UserRegistrationForm()
return render_template('user_registration.html', title="...", form=register_form)
The problem I am currently trying to solve is that I have base Jinja template layout.html, which is used in blueprints Jinja template user_registration.html like this:
{# user_registration.html #}
{% extends "layout.html" %}
But the layout.html is using some global variables of Flask app via flask.g and also config of app. When I try to load page with user_registration.html template (/user/register), it prints out error:
jinja2.exceptions.UndefinedError: 'ac' is undefined
The ac variable is defined inside of another route in main_app.py like g.ac = "...". I managed to solve this by adding parameter to template rendering inside of register_user() method as render_template(..., ac=g.ac), but it is not really elegant way of solving this issue, because there are also some other variables like config of app (also used in layout.html), which brings another error:
jinja2.exceptions.UndefinedError: 'flask.config.Config object' has no attribute 'login'
There must be some better way of handling this issue and passing base app attributes and global variables from flask.g inside of blueprint Jinja template. I've read multiple threads but cannot find similar issue.
I'm trying to upload multiple images using WTForms in Flask using "MultiFileField, however, it returns a string instead of the file object.
So I tried using the below:
request.files.getlist(form.upload_field.data)
But it returns an empty list, so anyway I can handle this to save the photos to a directory
There is documentation of file uploads with Flask here, and you are going about it the right way through accessing the request.files object. I've come across two ways to get an empty list back from there:
1. enctype form html attribute not set
Here's an example template that renders the MultipleFileField():
template = """
<form action="" method="POST" enctype="multipart/form-data">
{{ form.upload_field() }}
{{ form.submit() }}
</form>
"""
If I remove the enctype=... part, the list of files returns empty, where it otherwise would have values. A page on the internet says:
This value is required when you are using forms that have a file upload control
2. Passing the wrong Key to request.files.getlist()
request.files is a werkzeug.MultiDict, which is a mapping of keys to values, designed to handle having multiple values for the same key.
Using the same form template as above, inspecting the keys of request.files (print(list(request.files.keys()))) upon POST reveals ['upload_field'].
werkzeug.MultiDict.getlist has a single required parameter:
key - The key to be looked up.
So the only key in the MultiDict instance at this point is the string 'upload_field', if we want to get anything back from the getlist method, this needs to be the key that we pass to getlist. In your example code, you pass the value of the form.upload_field.data attribute (which in my tests is None). Change that to 'upload_field' and you should be away.
Here's a working minimal example that will print the result of calling request.files.getlist() upon form submit. Run the script, visit http://127.0.0.1:5000 in your browser, upload a couple of files and watch the terminal output.
from flask import Flask, render_template_string, request
from wtforms import Form, MultipleFileField, SubmitField
app = Flask(__name__)
class MyForm(Form):
upload_field = MultipleFileField()
submit = SubmitField()
template = """
<form action="" method="POST" enctype="multipart/form-data">
{{ form.upload_field() }}
{{ form.submit() }}
</form>
"""
#app.route("/", methods=["GET", "POST"])
def route():
form = MyForm()
if request.method == "POST":
print(request.files.getlist("upload_field"))
return render_template_string(template, form=form)
if __name__ == "__main__":
app.run(debug=True)
i am unable to set the environment variables from jinja2
<body>
<h1>hi {{session['user']}}<h1>
{{session['user']='Mad'}}
</body>
<body>
<h1>
hi {{session['user']}}
<h1>
{{session['user']='Mad'}}
</body>
I have already declared session['user'] in python code in flask but i am unable to set it in jinja2 that is the with the following html code
As described above, you need to define your variables within your backend and pass them to your template. For example:
run.py
from flask import Flask, render_template, session
app = Flask(__name__)
app.secret_key = 'your-secret-key'
#app.route("/<user>")
def user_page(user):
session['user'] = user
return render_template("user_page.html", user=session['user'])
if __name__ == '__main__':
app.run()
user_page.html
<body>
<h1>hi {{ user }}<h1>
</body>
Doing this doesn't make sense. Jinja2 is an HTML templating system, used server-side to render a page; it is not meant to update global variable. The logic should be implemented in your python code, before calling the template. The repsonsibility is thus:
python method: does server-side logic and prepare the variables needed by Jinja2 to render the response page;
jinja2 template: simply render the page using the variables prepared in 1.
Hence, editing session content should never appear in Jinja2 (it wouldn't make any sense anyway), so move your line to the python code. If you need two different names, simply save the old session value to a new variable that you pass to jinja2 alongside the updated session.
I'm completely new to flask, and really am completely lost with how to approach this. I've looked into other SO questions but I can't seem to get this working regardless.
I have a form as such:
<form class="teamSelection" method="POST" action="/submitted">
<select class="teamForm" id="teamDropdownSelector" type="text" name="teamDropdown" placeholder="Select A Team">
<option disabled selected>Select a game</option>
<option id="WatfordVSManchester Utd" value="">Watford VS Manchester Utd</option>
</select>
<input class="btn" type="submit" value="submit">
</form>
and my flask as so:
from flask import Flask
app = Flask(__name__)
#app.route("/submitted")
def hello():
return "hello world"
The goal is to take the content of the selected/submitted dropdown item, pass this to the flask file where I then use the team names to scrape information about the match. However at the moment I can't even seem to get the POST of the form to work and am at a complete loss. I appreciate this is a pretty vague and open-ended question, but I seriously don't know how else to figure this out.
Should I instead use jquery to detect when the dropdown has changed and use AJAX to send a POST to somehow call the script and pass the values into it?
Any help would be greatly appreciated.
EDIT
I thought I put this in the original post, but must have forgot.
I am currently running an apache localhost server, and am working with flask via pycharm. All I've done at the moment is install the flask package in pycharm, and haven't set any of it up like I've seen in some tutorials do when running from the command line. I assumed this step wasn't necessary, as I already have a server up and running with apache?
When it comes to backend stuff like this I really have no idea, so apologies if that's a stupid assumption.
I've changed the flask to:
from flask import Flask
app = Flask(__name__)
#app.route("/submitted", methods=['POST'])
def hello():
with open("newTest.csv", mode="w+") as file:
fileWriter = csv.writer(file)
fileWriter.writerow(['Time', 'HomeTeam', 'AwayTeam'])
file.close()
The reason being as I can see if this script is actually being called, if it is it will make a new csv file called newTest. After running the webpage and submitting no new csv file appears, so this script isn't being run, meaning it's likely due to me not configuring flask correctly?/The assumption that apache was enough was incorrect?
You have just to tell the flask method to accept POST request and to read parameters from the request
Example:
from flask import Flask, request
app = Flask(__name__)
#app.route("/submitted", methods=['POST'])
def hello():
myvariable = request.form.get("teamDropdown")
... your code ...
return "hello world"
So, your question is not about flask, but about fopen - you have to add a full file path including directory path script_dir = path.dirname(path.abspath(__file__)).
Flask script (modified for launching in my local copy of project):
from flask import Flask, render_template, request
import csv
from os import path
app = Flask(__name__)
script_dir = path.dirname(path.abspath(__file__))
#app.route ("/")
def index():
return render_template("index.html")
#app.route("/submitted", methods=["GET", "POST"])
def hello():
if request.method == "GET":
return render_template("index.html")
filefullpath = script_dir + '//newTest.csv'
with open(filefullpath, mode="w+") as file:
fileWriter = csv.writer(file)
fileWriter.writerow(['Time', 'HomeTeam', 'AwayTeam'])
file.close()
return "hello world"
index.html (in folder "/templates")
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
Test
<br>
<form class="teamSelection" method="POST" action="/submitted">
<select class="teamForm" id="teamDropdownSelector" type="text" name="teamDropdown" placeholder="Select A Team">
<option disabled selected>Select a game</option>
<option id="WatfordVSManchester Utd" value="">Watford VS Manchester Utd</option>
</select>
<input class="btn" type="submit" value="submit">
</form>
</body>
</html>
Modify your code as:
from flask import Flask
app = Flask(__name__)
#app.route("/submitted", methods=['POST'])
def hello():
return request.form['teamDropdown']
Please let me know if that helps.
I have an HTML file that has a web page design with a single form, for a user to enter his name. I want to create an six entry array for every submission (to later be filled with information on another page)
Is Django the proper utility to use for this? I would like to have the html design file and the python back end processing as separate files. If so, can anyone point me towards a good place to read about integrating HTML and underlying python codes that process HTML submission forms?
Django may be overkill for this. If all you want is a way to link a form to some backend Python code, a micro framework like Flask might be a better choice.
Here is how you do a simple form with Flask:
Create a directory project and inside it, a directory templates
Your template is simple:
{% if name %}
Hello {{ name }}
{% endif %}
<form method="POST">
<input type="text" name="name" value="Enter your name">
<input type="submit">
</form>
Save that as index.html in the templates subdirectory.
Create a file called go.py in the project directory, and in it copy and paste this:
from flask import Flask
from flask import render_template
from flask import request
app = Flask(__name__)
#app.route('/',methods=['POST','GET'])
def process_form():
if request.method == 'POST':
form_input = request.form['name']
return render_template('index.html',name=form_input)
else:
return render_template('index.html')
if __name__ == '__main__':
app.run(debug=True)
Finally from the project directory, type:
python go.py
Open your browser and go to http://localhost:5000/
You can create html form in Django, though you may have to convert the form to a template.
In case this you first time to use django, you may go though the official Django book
Django provides a template system (in which the presentation files are separate from the business logic and the entire system is highly decoupled). The template language is simple (but very powerful) text substitution on top of an existing text file. You can use the Django templates in HTML (full or partial), text, XML, JSON, or nearly any other text-based format.
Django also allows you to hook into another template system, but I don't know a lot about how that works.
Look at the Django template documentation for more information.