How do i create a progress bar with flask? - python

I'm developing a web interface by using flask.
The user log and reach an html page where a file can be upload. This file will be save and parse by an internal program (no relation with my problem)
I use a File Field in a Flask Form:
class MyForm(FlaskForm):
file = FileField("DROP YOUR FILE HERE", validators=[DataRequired()])
submit = SubmitField('Submit')
I call it in my main programm:
#app.route('/upload', methods=['GET', 'POST'])
#login_required
def uploadfile():
form = MyForm()
if form.validate_on_submit():
file = form.file
file.data.save(os.path.join("path/to/", secure_filename("filename")))
flash("File added")
return redirect(url_for('uploadfile'))
return render_template("upload.html",form=form)
and here is the body of my html page:
<form action="{{url_for('uploadfile')}}" method="post" enctype="multipart/form-data" novalidate>
{% with messages = get_flashed_messages() %}
{% if messages %}
{% for message in messages %}
<p class="error">{{ message }}</p>
{% endfor %}
{% endif %}
{% endwith %}
{{ form.csrf_token }}
{{form.fichier}}
{% for error in form.fichier.errors %}
<span style="color: red;">[{{ error }}]</span>
{% endfor %}
<p>{{ form.submit() }}</p>
</form>
The problem is uploading and saving a lot of data take lots of time.
So i want to add a progress bar but i don't know how to get the download progress and send it to my html progress bar.
This is why i ask your help. If you have any solution i will take it.
Thanks you

Related

What is the method to recognize new files in a directory using Flask?

So I have a form that uploads a file to a /assets/models and I have another form on a separate page that has a selection field where I want to select one of the files from /assets/models. However, unless I restart the Flask server it doesn't show a new file in the dropdown list.
forms.py
class PredictForm(FlaskForm):
datasheet = FileField('CSV Dataset', validators=[DataRequired()])
myModels = os.listdir('C:/Users/Matt/Documents/graymatter-flask/assets/models/')
myChoices = []
for model in myModels:
myChoices.append((model,model))
model_name = SelectField('Select the Model', choices = myChoices, validators=[DataRequired()])
submit = SubmitField('Predict')
routes.py
#app.route('/predictdata', methods=['GET', 'POST'])
def predictdata():
form = PredictForm()
if form.validate_on_submit():
assets_dir = os.path.join(os.path.dirname(app.instance_path), 'assets')
input_data = form.datasheet.data
input_filename = secure_filename(input_data.filename)
model_name = form.model_name.data
input_data.save(os.path.join(assets_dir, 'tempdata', input_filename))
results_array = easy_ai_prediction.prediction(input_filename, model_name)
session['results'] = results_array.tolist()
os.remove('C:/Users/Matt/Documents/graymatter-flask/assets/tempdata/'+input_filename)
return redirect(url_for('results'))
return render_template('predictdata.html', title='Predict Data', form=form)
predictdata.html
{% extends "base.html" %}
{% block content %}
<h1>Predict</h1>
<form action="" enctype="multipart/form-data" method="post" novalidate>
{{ form.hidden_tag() }}
<p>
{{ form.datasheet.label }}<br>
{{ form.datasheet(size=32) }}<br>
{% for error in form.datasheet.errors %}
<span style="color: red;">[{{ error }}]</span>
{% endfor %}
</p>
<p>
{{ form.model_name.label }}<br>
{{ form.model_name(size=1) }}<br>
{% for error in form.model_name.errors %}
<span style="color: red;">[{{ error }}]</span>
{% endfor %}
</p>
<p>{{ form.submit }}</p>
</form>
{% endblock %}
I apologize if this is a really basic question, I've just started learning web development just for this project so I'm struggling a bit.

flask-wtforms show a popup after clicking on submit button

I am using flask wtform and i want to show a confirmation popup after pressing the submit button so user can see that their submission is successful.
forms.py:
post = FloatField('', validators=[DataRequired()])
submit = SubmitField('Quote')
.html:
<form action="{{ url_for('product', id=product.id) }}" method="post">
{{ form.csrf_token }}
<h4>
{{ form.post(class='input-text qty', maxlength='9', placeholder='Price') }}
{{ form.submit(class='button primary-btn', id='orderform') }}
</h4>
</form>
.
.
.
.
.
<script>
$(document).ready(function(){
$("orderform").submit(function(){
alert("Submitted");
});
});
</script>
When i press the submit button the value is added to the database but i don't get any confirmation popup.
Flask has a built-in functionality called Flash, it should be able to provide a feedback for your application, you just have to import this on your routes.py:
from flask import flash
Then, also on your routes.py file, when you want to call the message, just call the flash method:
flash("Your submission is successful")
And finally, at your .html, allow the page to receive the flashing messages:
{% with messages = get_flashed_messages() %}
{% if messages %}
{% for msgs in messages %}
<div class="alert alert-info" role="alert">
{{msgs}}
</div>
{% endfor %}
{% endif %}
{% endwith %}

Django Validation Error raised but not displayed for shopping cart app

I am running Django 2.2 and have written a simple shopping cart. I wish to validate two fields at the same time in such a way that both cannot be empty at the same time. In my forms.py,
from django import forms
class CartAddProductForm(forms.Form):
cc_handle = forms.CharField(required=False, label='CC Handle', empty_value='')
lc_handle = forms.CharField(required=False, label='LC Handle', empty_value='')
def clean(self):
cleaned_data = super().clean()
if cleaned_data.get('cc_handle') == '' and cleaned_data.get('lc_handle') == '':
print("issue detected")
raise forms.ValidationError('Either cc or lc handle is required.')
return cleaned_data
This is following the official Django docs on cleaning and validating fields that depend on each other. The print() statement above lets me know that the issue has been detected, i.e. both fields are empty. Running the Django server, I see that the issue was indeed detected but no validation error message was displayed on top of the originating page. The originating page is the product page that contains the product and a link to add the product to the shopping cart. Normally the validation error message is displayed at the top of the page.
According to the docs, the validation is done when is_valid() is called. So I put a diagnostic print of my views.py
from django.shortcuts import render, redirect, get_object_or_404
from django.views.decorators.http import require_POST
from shop.models import Product
from .cart import Cart
from .forms import CartAddProductForm
#require_POST
def cart_add(request, product_id):
cart = Cart(request)
product = get_object_or_404(Product, id=product_id)
form = CartAddProductForm(request.POST)
if form.is_valid():
cd = form.cleaned_data
cart.add(product=product,
cc_handle=cd['cc_handle'],
lc_handle=cd['lc_handle'])
else:
print('invalid form')
return redirect('cart:cart_detail')
And indeed the words 'invalid form' popped up. The code then takes me to the shopping cart. Instead, what I want is to be at the product page and show the validation error informing the reader that both fields cannot be empty. Is there a simple way of doing it?
For required=True fields in the forms, if I leave it blank, there will be a message popping up saying that I need to fill it in. So I want to do something similar except the validation requires that both fields cannot be empty.
This is different from this Stackoverflow answer because that is a registration form. You can redirect it to the same form whereas for this case, the CartAddProductForm is embedded in all the products page on the site. If possible, I want the validation to occur at the same stage as the field with required=True option.
The product/detail.html template looks like the following.
{% extends "shop/base.html" %}
{% load static %}
{% block title %}
{{ product.name }}
{% endblock %}
{% block content %}
<div class="product-detail">
<img src="{% if product.image %}{{ product.image.url }}{% else %}{% static "img/no_image.png" %}{% endif %}">
<h1>{{ product.name }}</h1>
<h2>{{ product.category }}</h2>
<p class="price">${{ product.price }}</p>
<form action="{% url "cart:cart_add" product.id %}" method="post">
{{ cart_product_form }}
{% csrf_token %}
<input type="submit" value="Add to cart">
</form>
{{ product.description|linebreaks }}
</div>
{% endblock %}
Adding this line in form template has cleared your issue.
{{ cart_product_form.non_field_errors }}
product/detail.html:
{% extends "shop/base.html" %}
{% load static %}
{% block title %}
{{ product.name }}
{% endblock %}
{% block content %}
<div class="product-detail">
<img src="{% if product.image %}{{ product.image.url }}{% else %}{% static "img/no_image.png" %}{% endif %}">
<h1>{{ product.name }}</h1>
<h2>{{ product.category }}</h2>
<p class="price">${{ product.price }}</p>
<form action="{% url "cart:cart_add" product.id %}" method="post">
{{ cart_product_form }}
{% csrf_token %}
{{ cart_product_form.non_field_errors }} // This line will raise validation errors
<input type="submit" value="Add to cart">
</form>
{{ product.description|linebreaks }}
</div>
{% endblock %}
Doc:(Copied from official documentation)
Note that any errors raised by your Form.clean() override will not be
associated with any field in particular. They go into a special
“field” (called all), which you can access via the
non_field_errors() method if you need to. If you want to attach errors
to a specific field in the form, you need to call add_error().
Your view is inconditionnally redirecting to cart_details so no surprise you don't see the validation errors - you'd have to render the invalid form for this. You should only redirect when the post succeeded.

Prevent input type url default behavior using Flask

I have input type url in a form and Im trying to get the list of validation errors in the same page, but I want the form first send the data to the server and then render the error list but when the input type is set to url and I enter some other text It shows a popup and say Enter valid Url
It does it on the client side before sending any data to the server
I can use it with js and preventDefault but how can I overcome this default behavior using Flask
Here is my code
from flask_wtf import Form
from wtforms.fields import StringField
from flask.ext.wtf.html5 import URLField
from wtforms.validators import DataRequired, url
class BookmarkForm(Form):
url = URLField('url', validators = [DataRequired(), url()])
description = StringField('description')
the main py file
#app.route('/add', methods = ['GET', 'POST'])
def add():
form = BookmarkForm()
if form.validate_on_submit():
url = form.url.data
description = form.description.data
store_bookmark(url, description)
flash('stored "{}"'.format(description))
return redirect(url_for('index'))
return render_template('add.html', form = form)
and the template
<form id='addUrl' action='' method='post'
{% if form.url.errors %} class ='error'
{% endif %}>
{{ form.hidden_tag() }}
<p>Please enter your bookmark here</p>
{{ form.url(size = 50) }}
<p>Please enter additional description</p>
{{ form.description(size = 50) }}
<ul>
{% for error in form.url.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
<button type='submit'>Submit</button>
</form>
It depends on the browser support of HTML5 and It causes client-side validation and prevents data to passed to the server. You can overcome that default behavior of the HTML5 URL field by adding the tag novalidate to your form.
Your template will look like this
<form id='addUrl' action='' method='post' novalidate
{% if form.url.errors %} class ='error'
{% endif %}>
{{ form.hidden_tag() }}
<p>Please enter your bookmark here</p>
{{ form.url(size = 50) }}
<p>Please enter additional description</p>
{{ form.description(size = 50) }}
{# <input type='text' name='url' required> #}
<ul>
{% for error in form.url.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
<button type='submit'>Submit</button>
</form>

unable to show custom error message in Jinja2 template

I'm writing a register user function in ( using Flask, Python, Jinja2) which i'm checking if a username (or email) is already present and if so shows an error to below TextField.
register code is:
#app.route('/register', methods=['GET', 'POST'])
def register():
form = SignupForm()
error = None
if form.validate_on_submit():
user_by_name = Users.query.filter_by(username=form.username.data).first()
user_by_email = Users.query.filter_by(email=form.email.data).first()
if user_by_name:
error = 'Username already taken. Choose another'
return render_template('register.html', form=form, error = error)
elif user_by_email:
error = 'Email already registered. Login or register with another Email'
return render_template('register.html', form=form, error = error)
else:
#Add user details to DB logic
return redirect(url_for('index'))
return render_template('register.html', form=form, error = error)
I have a macro defined in a file util.html
{% macro render_field(field) %}
<div class="control-group {% if field.errors %}error{% endif %}">
{% if kwargs.get('label', True) %}
{{ field.label(class="control-label") }}
{% endif %}
<div class="controls">
{{ field(**kwargs) }}
{% for error in field.errors %}
<p class="help-block">{{ error }}</p>
{% endfor %}
</div>
</div>
{% endmacro %}
and using this macro in register.html as:
{% from "util.html" import render_field %}
{% extends "base.html" %}
{% block content %}
<form method="post">
{{ form.hidden_tag() }}
{{ render_field(form.username, label=True, class="input-xlarge", autofocus="autofocus", errors=error) }}
{{ render_field(form.password, label=True, class="input-xlarge", autofocus="autofocus") }}
{{ render_field(form.confirm, label=True, class="input-xlarge", autofocus="autofocus") }}
{{ render_field(form.email, label=True, class="input-xlarge", autofocus="autofocus", errors=error) }}
<button class="btn" type="submit">Register</button>
</form>
{% endblock %}
Now when i test the localhost:5000/register with duplicate username or email address it doesn't show any error at all (also doesn't add any user into DB which is okay).
But when i enter any wrong email or leave any field blank it shows respective error, but not showing the error which i want to pass using register view.
Is there any Jinja2 related logic missing?
And why its showing errors related to blank field or wrong email but not which i try to pass on duplicate username etc.
Please suggest.
Well, you doing it wrong :)
{% for error in field.errors %}
<p class="help-block">{{ error }}</p>
{% endfor %}
This macro searches for error in respective field.errors
But you pass error context variable, which is not attached to the field.
The right way to solve this will be to add def validate_user to your form definition and check for everything in there.
If it goes wrong -> raise ValidationError(message)
See example here:
WTForms documentation
With this approach, your validate_on_submit() should not return True if it is not validated.
You can just flash the error like this
sample code
file: views.py
#app.route('/')
def index():
# some checks which provided error
if error:
flash(error)
return render_template('template.html')
file template.html
{% for category, msg in get_flashed_messages(with_categories=true) %}
<div class="category">{{ msg|safe }}</div>
{% endfor %}

Categories

Resources