I have a Flask App which starts a long function after validation a form. I added a flash message before that function but the message appears after the function has finished:
# main_app.py
app = Flask(__name__)
class NameForm(FlaskForm):
name = StringField('Email Adress', validators=[DataRequired()], default='test#gmail.com')
# some more fields
submit = SubmitField('Submit')
def run_my_long_task(name):
# do some computing
# save results to a file
# return a pandas dataframe with the results for plotting with bokeh
#app.route('/', methods=['GET', 'POST'])
def index():
form = NameForm()
if form.validate_on_submit():
name = form.name.data
# I want to display a message before the run starts
flash('Analysis started, please wait.')
# Start a time consuming analysis
run_my_long_task(name)
return render_template('index.html', form=form)
Here is my index.html which contains a container with the flash message handling:
<div class="container">
{% for message in get_flashed_messages() %}
<div class="alert alert-warning">
<button type="button" class="close" data-dismiss="alert">×</button>
{{ message }}
</div>
{% endfor %}
{% block page_content %}{% endblock %}
The message shows up successfully but after the run_my_long_task() has finished. How to show up this message while the job is running?
Edit: So in general I just want to inform the user about the start of the analysis. Not necessarily need to be a flash message.
Instead of using the flash function on the server-side, you might want to use some javascript to achieve this:(this is just an example, so it might seem very ugly)
<script>
document.querySelector('DOMContentLoaded', () => {
document.querySelector("#youform").addEventListener('submit', () => {
const messageRef = document.querySelector("#flash-message");
messageRef.innerHTML = 'the message';
messageRef.styles.display = 'block';
})
});
</script>
<div class="alert alert-warning" id="flash-message" style="display: hidden;">
<button type="button" class="close" data-dismiss="alert">×</button>
</div>
I came up using Bootstrap’s JavaScript modal plugin, which is enough for my needs
Related
I am trying to implement newsletter/email subscription for my project.
I created a model which only stores the email and the timestamp and uses SendGrid to send emails to the users who subscribed.
I want to include an unsubscribe button inside the emails I send them. When the user clicks unsubscribe link in the mail it appends the id of the value in db to the url and redirects to cancelsub.html where I am accessing it.
In cancelsub.html I have a form with a submit button which when a user clicks should delete the value from db. It is not working for some reason.
Models.py--
class NewsletterUser(models.Model):
email = models.EmailField(null=True)
date_added = models.DateTimeField(default=datetime.now)
def __str__(self):
return self.email
Views.py--
def NewsLetter(request):
if request.method == 'POST':
email_input = request.POST.get('email_value')
new = NewsletterUser(email=email_input)
new.save()
sendEmail(email_input)
return render(request,"pages/index.html")
def DeleteNewsLetter(request):
if request.method == 'POST':
del_id = request.POST.get('id_value')
NewsletterUser.objects.filter(id= del_id).delete()
return render(request, "newsletter/CancelSubscription.html")
cancelsub.html--
<form id="cancel-subscription-form" method="POST">
{% csrf_token %}
<div class="email-and-btn">
<button class="btn btn-danger mb-2 art-digest-btn" id="cancel-btn" type="submit" value="">Yes, Cancel It</button>
</div>
</form>
<script src="https://code.jquery.com/jquery-1.9.1.js"></script>
<script>
var current_url = window.location.href
var id = current_url.split('?')[1]
id_int = parseInt(id)
$("#cancel-btn").val(id_int);
$(document).on('submit','#cancel-subscription-form',function(e){
e.preventDefault();
$.ajax({
type:'POST',
url:'{% url "DeleteNewsLetter" %}',
data:
{
id_value: parseInt($("#cancel-btn").val()),
csrfmiddlewaretoken:$('input[name=csrfmiddlewaretoken]').val(),
},
success:function(){
}
})
});
</script>
</div>
urls.py--
urlpatterns = [
path('', views.NewsLetter, name='NewsLetter'),
path('CancelSubscription', views.CancelSubscription, name='CancelSubscription'),
path('', views.DeleteNewsLetter, name='DeleteNewsLetter'),
]
When I execute this code out, instead of deleting the value from database, it adds a blank value into the db. I'm confused as to why this is happening.
It'd be really helpful if anyone guide me where I went wrong!.
Thanks in advance!!
I understand that the URL that you send on the email is something like this: http://mywebsite.com/unsubscribe/?9
So you get the "9" with the javascript code. You don't need the javascript if you give a name to your value like this: http://mywebsite.com/unsubscribe/?user_id=9
Now, you can just doing this:
<form id="cancel-subscription-form" method="POST" action="{% url "DeleteNewsLetter" %}">
{% csrf_token %}
<div class="email-and-btn">
<button name="id_value" class="btn btn-danger mb-2 art-digest-btn" id="cancel-btn" type="submit" value="{{request.GET.user_id}}">Yes, Cancel It</button>
</div>
</form>
I think that your problem is in the javascript code, so simplify and deleting it probably your system works.
I was trying to make a multiroom app with flask and flask socket-io integration but i think something is offset as if I try to join another room on another tab it sends the info to all rooms and other rooms show someone has joined in that room and after that if I send message it also don't work.
here's my code
from flask_socketio import SocketIO, join_room
from colorama import Fore, Style
app = Flask(__name__)
socketio = SocketIO(app)
#app.route("/")
def home():
return render_template("index.html")
#app.route("/chat")
def chat():
username = request.args.get("username")
room = request.args.get("room")
if username and room:
return render_template("chat.html", username=username, room=room)
else:
return redirect(url_for("home"))
#socketio.on('join_room')
def handle_join_room(data):
info = "[INFO] "+f"[ROOM: {data['room']}]: "+f"User {data['username']} has joined"
print(Fore.CYAN+info)
join_room(data['room'])
socketio.emit('join_room_ann', data)
Style.RESET_ALL
#socketio.on('send_mess')
def handle_mess(data):
info = f"[MESSAGE][ROOM: {data['room']}][USER:{data['username']}]: {data['message']}"
print(info)
if __name__ == "__main__":
socketio.run(app, debug=True)}```
and html side
client
[{% extends "common.html" %}
{% block index %}
<div class="log left">
<p>Username:</p>
<p class="cls">{{username}}</p>
<p>Room:</p>
<p class="cls">{{room}}</p>
</div>
<div id="messages" class='bottoml log'>
<p>in room:</p>
</div>
<form action="" id="inp_form">
<div class="log chat">
<div class="in">
<input type="text" id="inp_box" class="chat_in" placeholder="Type your text">
<button type="submit">Send</button>
</div>
</div>
</form>
{% endblock %}
{% block chat %}
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.2.0/socket.io.js"></script>
<script>
const socket = io.connect("http://127.0.0.1:5000");
socket.on('connect', function(){
socket.emit("join_room",{
username:"{{username}}",
room:"{{room}}"
});
let inp_box = document.getElementById('inp_box');
document.getElementById('inp_form').onsubmit = function (e){
e.preventDefault();
mes = inp_box.value.trim();
if (mes.length){
socket.emit('send_mess', {
username:"{{username}}",
room:"{{room}}",
message:mess
})
}
inp_box.value='';
inp_box.focus();
}
});
socket.on('join_room_ann', function(data){
console.log(data);
const newnode = document.createElement('div');
newnode.innerHTML = `${data.username}`;
newnode.className+="newn"
document.getElementById('messages').appendChild(newnode);
})
</script>
{% endblock %}]
If you want to send the join room announcement just to the room in question, you have to indicate that in the emit call:
socketio.emit('join_room_ann', data, room=data['room'])
Without the room argument, the emit goes to all connected clients, regardless of what room they're in.
I have a made a quiz page, which checks whether the answer of the user is correct or not using a "checkans" function. I want to return a "Correct" message if the answer is correct and an "Incorrect" message if the answer is not correct. Now I can "kind of" do it, but not exactly what I want. Now it returns the message after redirecting to a whole new page, with the Question Box and everything else totally disappeared, only with the message.
I want the message to be shown on the same original question page, somewhere under the question box or within the question box, without redirecting to another page or refreshing the page, after submitting the answer. I don't know how to do it.
Here is my view:
class QuizView(generic.ListView):
template_name = 'geniusdennis/quiz.html'
queryset = Spanish.objects.all()
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
# grab the max id in the database
max_id = Spanish.objects.order_by('-id')[0].id
random_id = random.randint(1, max_id + 1)
random_spanish_question = Spanish.objects.filter(id__gte=random_id)[0]
context['random_spanish_question'] = random_spanish_question
return context
Here is my function for checking the answer:
def checkans(request, spanish_id):
random_spanish_question = get_object_or_404(Spanish, pk=spanish_id)
query = request.GET.get('ans')
coreng = random_spanish_question.english_set.get()
if query == str(coreng):
return render(request, 'geniusdennis/quiz.html',{
'message': "Correct!",
})
else:
return render(request, 'geniusdennis/quiz.html', {
'message': "Incorrect.",
'correct_answer': "The correct answer is " + str(coreng),
})
And here is my HTML page:
{% load static %}
<link rel="stylesheet" type="text/css" href="{% static 'geniusdennis/style.css' %}">
{% if random_spanish_question %}
<div class="flexcontainer" style="justify-content: center;">
<div class="sectiontitle">Quiz time
</div>
<div class="question_card">
<div class="question_word">{{ random_spanish_question }}</div>
<form action="/checkans/{{random_spanish_question.id}}/" method="get">{% csrf_token %}
<label for="ans">Answer:</label>
<input type="text" name="ans"/>
<input type="submit" value="Submit"/>
</form>
<input type="submit" value="Skip"/>
</div>
</div>
{% else %}
{% if message %}
<div class="message">
{{ message }}
</div>
<div class="ans">
{{ correct_answer }}
</div>
{% endif %}
{% endif %}
What you need is ajax, so you need some js code here.
<scrip src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script>
$('form').on('submit', function(e) { // or you can get the form by id if you set it
e.preventDefault(); // avoid to execute the actual submit of the form.
var form = $(this);
var url = form.attr('action');
$.ajax({
type: 'GET',
url: url,
data: form.serialize(), // serializes the forms elements.
success: function(data)
{
... // whatever you want to do
var alertMessage = data.message;
if (data.correct_answer) {
alertMessage += ' ' + data.correct_answer;
}
alert(alertMessage); // show response
}
});
});
</script>
html forms will go to action url. If you want some changes or functions in your page without reload, you need to use js.
Quite commonly in web applications, you need to display a one-time
notification message (also known as “flash message”) to the user after
processing a form or some other types of user input.
For this, Django provides full support for cookie- and session-based
messaging, for both anonymous and authenticated users. The messages
framework allows you to temporarily store messages in one request and
retrieve them for display in a subsequent request (usually the next
one). Every message is tagged with a specific level that determines
its priority (e.g., info, warning, or error).
for implementing messages refer to: https://docs.djangoproject.com/en/1.11/ref/contrib/messages/
I'm working on a web application using Flask in Python.
I have small function in my application that calculates some values in the background and displays the result on the web page via a flashing message.
Everything is displaying and working fine but it requires page reloading to get the flashing message.
I want to display messages without reloading page.
I heard that I can do that with js, but I'm not familiar with js.
If you have any ideas or suggestion I would appreciate.
There is my code that could build a better picture of what I'm doing.
This is the renderer between my app and the main html file
{% macro render_field(field) %}
<dt> {{ field.label }}
<dd> {{ field(**kwargs)|safe }}
{% if field.errors %}
<ul class=errors>
{% for error in field.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
</dd>
{% endmacro %}
This is the html file were I want to display flashing messages:
<div class="container-fluid" style="min-height:100%">
{% with messages = get_flashed_messages() %}
{% if messages %}
{% for message in messages %}
<div class="alert alert-warning alert-dismissible" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
{{message}}
</div>
{% endfor %}
{% endif %}
{% endwith %}
</div>
Here's what Flask Web Development: Developing Web Applications with Python (pp. 46-48) has to say of Message Flashing:
Sometimes it is useful to give the user a status update after a request is completed. This
could be a confirmation message, a warning, or an error. A typical example is when you
submit a login form to a website with a mistake and the server responds by rendering
the login form again with a message above it that informs you that your username or
password is invalid.
Flask includes this functionality as a core feature. Example 4-6 shows how the flash()
function can be used for this purpose.
Example 4-6. hello.py: Flashed messages
#app.route('/', methods=['GET', 'POST'])
def index():
form = Nameform()
if form.validate_on_submit():
old_name = session.get('name')
if old_name is not None and old_name != form.name.data:
flash('Looks like you have changed your name!')
session['name'] = form.name.data
form.name.data = ''
return redirect(url_for('index'))
return render_template('index.html', form=form, name=session.get('name'))
form = form, name = session.get('name'))
In this example, each time a name is submitted it is compared against the name stored
in the user session, which would have been put there during a previous submission of
the same form. If the two names are different, the flash() function is invoked with a
message to be displayed on the next response sent back to the client.
Calling flash() is not enough to get messages displayed; the templates used by the
application need to render these messages. The best place to render flashed messages is
the base template, because that will enable these messages in all pages. Flask makes a
get_flashed_messages() function available to templates to retrieve the messages and
render them, as shown in Example 4-7.
Example 4-7. templates/base.html: Flash message rendering
{% block content %}
<div class="container">
{% for message in get_flashed_messages() %}
<div class="alert alert-warning">
<button type="button" class="close" data-dismiss="alert">×</button>
{{ message }}
</div>
{% endfor %}
{% block page_content %}{% endblock %}
</div>
{% endblock %}
In this example, messages are rendered using Bootstrap’s alert CSS styles for warning
messages (one is shown in Figure 4-4).
Figure 4-4. Flashed message
A loop is used because there could be multiple messages queued for display, one for
each time flash() was called in the previous request cycle. Messages that are retrieved from get_flashed_messages() will not be returned the next time this function is called,
so flashed messages appear only once and are then discarded.
This is not possible via Python without reloading the page. You must do this in javascript. I suggest CSS styling with display: none and display: block. Here is an example.
1) Python Code, this should go in your app.py or flask.py file.
app.route('/flash/<message>')
def flash(message):
return render_template('flash.html', msg=message)
This will render the HTML page named flash.html. The URL passed in will also have another argument, <message> this is the message that will flash. A URL like this, localhost:80/flash/Hello%20World! will flash the message "Hello World!" on your screen.
There is also another way to pass a message in, this is will arguments. The code for that is like so.
app.route('/flash')
def flash():
message = request.args.get("msg")
return render_template("flash.html", ,msg=message)
This uses the flask's request arguments. So a URL like this, localhost:80/flash?msg=Hello%20World! will give a flashing message saying "Hello World!". If you want to use this method be sure to have the import statement, from flask import request in your import statements.
2) Html Code, this is a separate file named, flash.html in your templates folder.
<body>
<h1 id="header">{{ message }}</h1>
<script>
var heading = $("#header");
setInterval(function() {
if (heading.style.display == "block") { heading.style.display = "none"; }
else if (heading.style.display == "none") { heading.style.display = "block"; }
}, 1000);
</script>
</body>
The 1000 in the setInterval is milliseconds. So the heading will blink every 2 seconds.
You may want to consider using Toastr instead. I ran into the same roadblock with Flask's Flash feature, and Toastr is pure JS. You can use it just like a console log line in your code
toastr.info("Here's a message to briefly show to your user");
Good day, I have a simple web page with an email form in it. I'm trying to collect the data from it and populate a database without refreshing the template. Here is my code so far:
Form:
from flask_wtf import Form
class EmailForm(Form):
email = StringField('Email Address', [
DataRequired(message='Required field'),
Email(message='Please provide a valid email address')
])
submit = SubmitField('send')
Route:
#app.route('/', methods=('GET', 'POST'))
def index():
form = EmailForm(request.form)
if request.method == 'POST' and form.validate_on_submit():
try:
email = Email(form.data['email'])
db.session.add(email)
db.session.commit()
except IntegrityError as e:
app.logger.info(e)
return redirect(url_for('index'))
return render_template('index.html', form=form)
Ajax:
$(function() {
$('#email_submit').bind('click', function() {
$.getJSON('/', {
email: $('input[name="email"]').val()
});
return false;
});
});
Template:
<form name="collectEmail" id="collectForm" method="post" action="{{ url_for('index') }}">
{{ form.hidden_tag() }}
{{ form.csrf_token }}
{% if form.csrf_token.errors %}
<div class="warning">You have submitted an invalid CSRF token</div>
{% endif %}
<div class="input-group">
{{ form.email(class='form-control', placeholder='Your Email *', type='email')}}
<p class="help-block text-danger"></p>
<span class="input-group-btn">
{{ form.submit(class='btn btn-primary', id='email_submit', type='submit') }}
</span>
</div>
</form>
The database successfully populates; but, I would like to avoid refreshing the page after submitting the form.
You are not sending the request with AJAX, #email_submit is an input of type submit, not a button, so if you don't use preventDefault() you end up executing the default behaviour of that input.
You have 2 options there, one is using preventDefault() and the other is to switch that input to a button, so it won't submit the form before the javascript code runs.