Passing argument to a flask GET function - python

I'm trying to write a simple messaging function and, despite searching, having trouble coming up with a workable solution. On the get function, the code retrieves stored messages and passes this to be rendered.
The post function stores the message which all works fine, however I'm trying to redirect the user back to the original page they came from to display the new message, hence trying to pass the receiving user variable to the get function.
My code so far below. I'm new to flask so would appreciate any other pointers as well. Thanks in advance!
#app.route("/messaging", methods=["GET", "POST"])
#login_required
def messaging():
if request.method == "GET":
clicked_user = request.args.get('user')
messages = db.execute("SELECT * FROM messages WHERE (m_sending_user = :self_user AND m_receiving_user = :other_user) OR (m_sending_user = :other_user AND m_receiving_user = :self_user)",
self_user = session["user_id"],
other_user = clicked_user)
return render_template("messaging.html", messages=messages, clicked_user=clicked_user)
if request.method == "POST":
receiving_user = request.form.get("recipient")
db.execute("INSERT INTO messages (m_sending_user, m_receiving_user, message) VALUES (:m_sending_user, :m_receiving_user, :message)",
m_sending_user = session["user_id"],
m_receiving_user = receiving_user,
message = request.form.get("reply"))
flash("Sent!")
return render_template("messaging.html")

Why do you not simply have the post function also call the get function like so
def get_function(clicked_user):
messages = db.execute("SELECT * FROM messages WHERE (m_sending_user = :self_user AND m_receiving_user = :other_user) OR (m_sending_user = :other_user AND m_receiving_user = :self_user)",
self_user = session["user_id"],
other_user = clicked_user)
return render_template("messaging.html", messages=messages, clicked_user=clicked_user)
#app.route("/messaging", methods=["GET", "POST"])
#login_required
def messaging():
if request.method == "GET":
clicked_user = request.args.get('user')
return get_function(clicked_user)
if request.method == "POST":
receiving_user = request.form.get("recipient")
db.execute("INSERT INTO messages (m_sending_user, m_receiving_user, message) VALUES (:m_sending_user, :m_receiving_user, :message)",
m_sending_user = session["user_id"],
m_receiving_user = receiving_user,
message = request.form.get("reply"))
flash("Sent!")
return get_function(m_receiving_user)
But of course with better named functions ;)

If you send data in GET request, it will be visible in URL parameter. Rather, you can use session object to store variable. More on session object can be found in the [official documentation on session object].(https://flask.palletsprojects.com/en/1.1.x/quickstart/#sessions)
You can use url_for with redirect method to redirect user to the original template.
Here I have shown a basic example:
messaging.html:
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title>Post request redirect</title>
</head>
<body>
{% with messages = get_flashed_messages() %}
{% if messages %}
<ul>
{% for message in messages %}
<li>{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
{% endwith %}
{% if clicked_user %}
Clicked user: {{ clicked_user }}
{% endif %}
{% if messages %}
Messages: {{ messages }}
{% endif %}
<h3>Send a Message</h3>
<form action='{{url_for("messaging")}}' method="post">
Recipient:
<input type="text" name="recipient">
<input type="submit" value="Submit">
</form>
</body>
</html>
app.py:
from flask import Flask, render_template, redirect, url_for, session, request, flash
app = Flask(__name__)
# Set the secret key to some random bytes. Keep this really secret!
app.secret_key = b'_5#y2L"F4Q8z\n\xec]/'
#app.route("/messaging", methods=["GET", "POST"])
def messaging():
if request.method == "GET":
data = {
"shovon": "some messages for shovon",
"arsho": "other messages for arsho"
}
receiving_user = None
messages = None
if 'receiving_user' in session:
receiving_user = session["receiving_user"]
messages = data.get(receiving_user)
return render_template("messaging.html", messages=messages, clicked_user=receiving_user)
if request.method == "POST":
receiving_user = request.form.get("recipient")
session["receiving_user"] = receiving_user
flash("Sent!")
return redirect(url_for("messaging"))
Output:
Before form submission:
After form submission:
N.B.: Here I have mocked the database queries with static dictionary search.

Related

validate_on_submit() not working in Flask. What should I do?

I am new to Flask.
The validate_on_submit() is not working and I also don't know what app.app_context() and app.test_request_context() do in my code.
The only thing I am trying to do is get my form to validate I can't figure out why it's not working.
This is my main.py
from flask import Flask, render_template, request, flash
from the_first_wt_form import MyForm
app = Flask(__name__)
app.config['SECRET_KEY'] = '934kegn298u54kjgnjdkrbg9u939'
with app.app_context():
with app.test_request_context():
a_form = MyForm()
#app.route('/', methods=['GET', 'POST'])
def home():
if request.method == "POST":
name = request.form['name']
print(name)
email = request.form['email']
print(email)
passwrd = request.form['password']
print(passwrd)
con = request.form['confirm']
print(con)
if a_form.validate_on_submit():
print("Good job")
name = request.name.data
print(name)
else:
print('We messed up')
if a_form.errors != {}:
for err in a_form.errors.values():
print(f"There was an error with creating user: {err}")
flash(f"There was an error with creating user: {err}", category='danger')
return render_template('mynewhome.html', form=a_form)
if __name__ == "__main__":
app.run(debug=True)
This is the code from My wt_form.py
from wtforms import StringField, PasswordField, validators, SubmitField
from flask_wtf import FlaskForm
class MyForm(FlaskForm):
name = StringField('name', [validators.Length(min=4, max=25), validators.DataRequired()])
email = StringField('Email Address', [validators.Length(min=6, max=35), validators.Email()])
password = PasswordField('New Password', [
validators.DataRequired(),
validators.EqualTo('confirm', message='Passwords must match')
])
confirm = PasswordField('Repeat Password')
submit = SubmitField('Register')
And Finally this is mynewhome.html
<!DOCTYPE html> <html lang="en"> <head>
<meta charset="UTF-8">
<title>How are you?</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap#5.1.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-uWxY/CJNBR+1zjPWmfnSnVxwRheevXITnMqoEIeG1LJrdI0GlVs/9cVSyPYXdcSF" crossorigin="anonymous">
</head> <body>
<h1> Hello BRo </h1> <br><br> {% with messages = get_flashed_messages(with_categories = true) %}
{% if messages %}
{% for category, message in messages %}
<div class="alert alert--{{ category }}">
<button type="button" class="m1-2 mb-1 close" data-dismiss="alert" aria-label="Close">
{{ message }}
<span aria-hidden="true">×</span>
</button>
</div>
{% endfor %}
{% endif %}
{% endwith %} <br><br>
<div class="container">
<form method="POST" action="/" class="form-register">
{{ form.hidden_tag() }}
{{ form.name.label }} {{ form.name(class = "form-control", Placeholder = "Usern Name") }}
{{ form.email.label }} {{ form.email(class = "form-control", Placeholder = "Email Address") }}
{{ form.password.label }} {{ form.password(class = "form-control", Placeholder = "Password") }}
{{ form.confirm.label }} {{ form.confirm(class = "form-control", Placeholder = "Confirm Password") }}
<br>
{{ form.submit(class = "btn btn-lg btn-block btn-primary") }}
</form> </div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap#5.1.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-kQtW33rZJAHjgefvhyyzcGF3C5TFyBQBA13V1RKPf4uH+bwyzQxZ6CmMZHmNBEfJ" crossorigin="anonymous"></script> </body> </html>
As a new flask user and simple flask usage, you should not need app_context and test_request_context. You can have a look on the documentation to understand them, but you don't need them in this situation.
You have to instantiate your form in the view function home
It is also a better practice to use form data only after validation, as you never know what the user enter in your form.
In the import you were importing the_first_wt_form but you said your file is called wt_form so I made the appropriate changes. But accordingly to your modules setup it might be wrong.
The main.py should look like (I tested it):
from flask import Flask, render_template, request, flash
from wt_form import MyForm
app = Flask(__name__)
app.config['SECRET_KEY'] = '934kegn298u54kjgnjdkrbg9u939'
#app.route('/', methods=['GET', 'POST'])
def home():
a_form = MyForm()
if request.method == "POST":
if a_form.validate_on_submit():
print("Good job")
name = a_form.name.data
print(name)
# (...)
else:
print('We messed up')
if a_form.errors != {}:
for err in a_form.errors.values():
print(f"There was an error with creating user: {err}")
flash(f"There was an error with creating user: {err}", category='danger')
return render_template('mynewhome.html', form=a_form)
if __name__ == "__main__":
app.run(debug=True)
Note that you can access data from the a_form instance directly rather than on the request instance.

redirect(url_for()) passing parametrs doesn't work

whenver I pass a parameter using redirect and url_for, it addes them to the url but not showing them in the html. If I use render_template and passing parameters it's working as it should.
My Code:
#app.route("/forgotpass", methods = ["GET", "POST"])
def forgot_password():
if session["logged_in"] == True:
return redirect(url_for("profile"))
if request.method == "POST":
email = request.form["username"]
email = email.lower()
logins.forgotPassword(email)
return redirect(url_for("login", error = "We have sent you an email with your new password!"))
elif request.method == "GET":
return render_template("forgotPassword.html")
HTML:
<p>{{ error | default('') }}</p>
Does anyone know how can I fix it?
That is how url_for works, you can pass parameters/variables to it and those will be added to the URL, it is not made for displaying errors.
If you want to display messages to user, for example after successful login or when login fails, you should use Message Flashing instead: https://flask.palletsprojects.com/en/1.1.x/patterns/flashing/
Example code, import flash from flask first:
#app.route("/forgotpass", methods = ["GET", "POST"])
def forgot_password():
if session["logged_in"] == True:
return redirect(url_for("profile"))
if request.method == "POST":
email = request.form["username"]
email = email.lower()
logins.forgotPassword(email)
flash('We have sent you an email with your new password!')
return redirect(url_for("login"))
elif request.method == "GET":
return render_template("forgotPassword.html")
Put this somewhere in your login template:
{% with messages = get_flashed_messages() %}
{% if messages %}
<ul class="flashes">
{% for message in messages %}
<li>{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
{% endwith %}

SelectField : why returning "Not a valid choice error"?

I'm pretty noob in python, so it's probably a stupid question, sorry for that.
contact.html
{% extends 'layout.html' %}
{% block content %}
<form method="POST" action="/">
<fieldset class="form-field">
{{ form.iro.label }}<br>
{{ form.iro }}
{% if form.iro.errors %}
<ul class="errors">
{% for error in form.iro.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
</fieldset><br>
{{ form.sub_issue }}
</form>
</div>
{% endblock %}
forms.py
from flask_wtf import FlaskForm
from wtforms import StringField, TextField, SubmitField, FieldList, FormField, SelectField, Form
from wtforms.validators import DataRequired, Length, Optional
class ContactForm(FlaskForm):
"""Contact form."""
iro = SelectField(
'IRO',
choices=[]
)
sub_issue = SubmitField('Open an issue')
in my code, I populate the choices argument of SelectField with a json from a request
#app.route('/', methods=('GET', 'POST'))
def contact():
form = ContactForm()
if request.method == 'GET':
r = requests.get("https://...",verify=False)
form.iro.choices= [(iro,iro) for iro in list(r.json().values())]
return render_template('contact.html', form=form)
else:
if form.validate_on_submit():
#code when submitted...
But when I click on the submit button, a "Not a valid choice" error appears under my SelectField
Do you know why ?
I can't test it but for me all problem is that you add values to choices only in GET. And when you run validate_on_submit(): for POST then choices is empty and it will always give "Not a valid choice"
You would need something like this.
#app.route('/', methods=('GET', 'POST'))
def contact():
form = ContactForm()
r = requests.get("https://...", verify=False)
form.iro.choices = [(iro,iro) for iro in list(r.json().values())]
if request.method == 'GET':
return render_template('contact.html', form=form)
else:
if form.validate_on_submit():
#code when submitted...
If requests always get the same values then you should read them only once at start (outside contact()), write in local file or database and read them in contact()
If requests may gives different values then you should read them in GET, save in file with uniqe name (or in database with uniqe ID) and send this name/ID to HTML as hidden field. And later in POST you should use this unique name/ID to read from file/database.
More or less
#app.route('/', methods=('GET', 'POST'))
def contact():
form = ContactForm()
if request.method == 'GET':
r = requests.get("https://...", verify=False)
form.iro.choices = [(iro,iro) for iro in list(r.json().values())]
save r.json() in unique file
form.hidden_field = unique_file
return render_template('contact.html', form=form)
else:
unique_file = form.hidden_field
read r.json() from unique file
form.iro.choices = [(iro,iro) for iro in list(r.json().values())]
if form.validate_on_submit():
#code when submitted...

Unable to add list to session in flask

I have simple input field where user is adding note.. and what ever note user is adding it will be displayed above in the list.. and m trying to store in a session in flask
This is my flask code
from flask import Flask, render_template, request, session
from flask_session import Session
app = Flask(__name__)
app.config["SESSION_PERMANENT"] = False
app.config["SESSION_TYPE"] = "filesystem"
Session(app)
#app.route("/", methods=["GET", "POST"])
def index():
if session.get("notes") is None:
session["notes"]=[]
if request.method == "POST":
note = request.form.get("note")
session["notes"].append(note)
return render_template("session-index.html", notes=session["notes"])
if __name__ == "__main__":
app.run(debug=True)
And this is my html page
{% extends "inherit-layout.html" %}
{% block heading %}
Form Page
{% endblock %}
{% block body %}
<ul>
{% for note in notes %}
<li>{{ note }}</li>
{% endfor %}
</ul>
<form action="{{ url_for('index') }}" method="POST">
<input type="text" name="note" placeholder="Enter Note Here">
<button type="submit">Add Note</button>
</form>
{% endblock %}
What currently happening is when m adding note for second time it is replacing previous one in the list.
from flask import *
app = Flask(__name__)
app.config["SESSION_PERMANENT"] = False
app.config["SESSION_TYPE"] = "filesystem"
app.config['SECRET_KEY'] = "asddaffffa"
#app.route("/", methods=["GET", "POST"])
def index():
if session.get("notes") is None:
session["notes"]=[]
if request.method == "POST":
note = request.form.get("note")
list = session["notes"]
list.append(note)
session["notes"] = list
return render_template("session-index.html", notes=session["notes"])
if __name__ == "__main__":
app.run(debug=True)
This should work..

Python - flask - flashed messages are skipped (not appearing) and webserver returns responses straight away

I am very new to Python flask and webserver concept in general but have managed to create a simple webapp.
It basically consists of a home page with a form to fill in and a submit button which returns a flashed messages when : 1) all fields are filled correctly and 2) not all fields are filled.
My code looks like this:
#app.route('/', methods = ['GET', 'POST'])
def home():
form = preference_form(request.form)
print(form.errors)
if request.method == 'POST':
name = request.form['name']
age = request.form['age']
address = request.form['distance']
date = request.form['date']
if form.validate():
flash('All good!')
else:
flash('Oops! All details are required to provide accurate results!')
return render_template('home.html', form = form)
#app.route('/results', methods = ['POST'])
def return_results():
name = request.form.get('name')
age = request.form.get('age')
address = request.form.get('address')
result = function_a(name=name,
age=age,
address=address)
return result
The flash messages worked as desired but after I added the return_results() route, after the form is filled (whether complete or incomplete) it brings me straight to /results.
Could you please tell me what am I missing here?
Thank you!
You should render your flash messages in your template file. Add following code snippet in your template which you want to show the flash messages or your layout template file:
{% with messages = get_flashed_messages() %}
{% if messages %}
<ul class=flashes>
{% for message in messages %}
<li>{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
{% endwith %}
UPDATE: One view function with three execute path will meet your needs.
1)if the request method is get, just render the home.html without flash message;
2)if the request method is post and the form data pass the validation, render the
result.html with success flash message;
3)if the request method is post but the form data doesn't pass the validation, render the home.html again with the failed flash message.
#app.route('/', methods=["GET", "POST"]
def index():
form = PreferenceForm()
if request.method == "POST":
if form.validate_on_submit():
flash("success.")
result = func(age=form.age.data,
date=form.date.data,
name=form.name.data,
address=form.address.data)
return render_template("result.html", result=result)
else:
flash("failed!")
return render_template('home.html', form=form)

Categories

Resources