Issue
Background and Problem
I'm trying to implement a message flashing function when users both correctly and incorrectly enter input to the forms on a web application using Flask.
It was succeeded to insert data to database from forms when I execute the following programs.
However, it is failed to call message flashing and there was no error message. Only the inserted data was shown after clicking the button "Create" and no message flashing on my current programs.
How should I fix my programs?
Official Document
Reading the official document of Flask Message Flashing!, I am not sure about how should I use the secret key for my programs.
Programs
For execution
$ FLASK_APP=app.py FLASK_DEBUG=true flask run
app.py
from flask import Flask, flash, render_template, request, redirect, url_for, abort, jsonify
from flask_sqlalchemy import SQLAlchemy
import sys
app = Flask(__name__)
app.secret_key = b'_5#y2L"F4Q8z\n\xec]/'
app.config['SQLALCHEMY_DATABASE_URI'] = 'postgres://username@localhost:5432/sample'
db = SQLAlchemy(app)
class Todo(db.Model):
__tablename__ = 'todos'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String)
city = db.Column(db.String(120))
# TODO: implement any missing fields, as a database migration using Flask-Migrate
def __repr__(self):
return f'<Todo {self.id} {self.name} {self.city}>'
db.create_all()
@app.route('/todos/create', methods=['POST'])
def create_todo():
error = False
body = {}
try:
name = request.form['name']
city = request.form['city']
todo = Todo(name=name, city=city)
if name == "":
flash("write your name", "failed")
elif city == "":
flash("write your city", "failed")
db.session.add(todo)
db.session.commit()
body['name'] = todo.name
body['city'] = todo.city
flash("submitted", "success")
except:
error = True
db.session.rollback()
print(sys.exc_info())
finally:
db.session.close()
if error:
abort (400)
else:
return jsonify(body)
@app.route('/')
def index():
return render_template('index.html', data=Todo.query.all())
index.html
<html>
<head>
<title>Text App</title>
<style>
.hidden{
display: none;
}
</style>
</head>
<body>
<form method="post" action="/todos/create">
<h4>name</h4>
<input type= "text" name="name" />
<h4>city</h4>
<input type= "text" name="city" />
<input type= "submit" value="Create" />
</form>
<div id= "error" class="hidden">Something went wrong!</div>
<ul>
{% for d in data %}
<li>{{d.name}}</li>
<li>{{d.city}}</li>
{% endfor %}
</ul>
<script>
const nameInput = document.getElementById('name');
const cityInput = document.getElementById('city');
document.getElementById('form').onsubmit = function(e) {
e.preventDefault();
const name = nameInput.value;
const city = cityInput.value;
descInput.value = '';
fetch('/todos/create', {
method: 'POST',
body: JSON.stringify({
'name': name,
'city': city,
}),
headers: {
'Content-Type': 'application/json',
}
})
.then(response => response.json())
.then(jsonResponse => {
console.log('response', jsonResponse);
li = document.createElement('li');
li.innerText = name;
li.innerText = city;
document.getElementById('todos').appendChild(li);
document.getElementById('error').className = 'hidden';
})
.catch(function() {
document.getElementById('error').className = '';
})
}
</script>
</body>
</html>
Environment
Python 3.6.0
Flask 1.1.1
SQLAlchemy 1.3.10
psql 11.5
Solution
Flashed messages won't automatically render, you have to do it. Here is the example jinja template code from the docs:
{% with messages = get_flashed_messages() %}
{% if messages %}
<ul class=flashes>
{% for message in messages %}
<li>{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
{% endwith %}
The usual pattern is to have that in a base template that other templates extend from so that flashed messages will display wherever they are registered. The docs go through all of that.
However, the flashed messages require a page load for the template to render but you are using AJAX to submit form and are dynamically rendering output, so the messages won't flash until the user refreshes, or navigates away.
Instead of using flash to communicate to the user that they didn't fill in required fields, consider setting the required
attribute of the <input>
fields:
<input type= "text" name="name" required/>
Answered By - SuperShoot
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.