How to handle errors in Django - python

I want to make my django app as user friendly as possible and I want to handle appropriate errors and have it push out an error message sort of like an alert in javascript. I want to do this when there's no file uploaded. So when the upload button is pressed and nothing have been uploaded there would be an alert message sent out.
My view, views.py:
def upload(request):
if "GET" == request.method:
return render(request, 'uploadpage/upload.html', {})
else:
excel_file = request.FILES["excel_file"]
# you may put validations here to check extension or file size
wb = openpyxl.load_workbook(excel_file)
# getting a particular sheet by name out of many sheets
worksheet = wb['Summary']
# iterating over the rows and
# getting value from each cell in row
seller_info = []
for cells in worksheet.iter_rows(min_col=2, max_col=2, min_row=1, max_row=5):
for cell in cells:
seller_info.append(str(cell.value))
return render(request, 'uploadpage/upload.html', {"excel_data": seller_info})
My template, uploadpage/upload.html:
<!DOCTYPE html>
<html>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<link rel="stylesheet" href="{% static 'css/upload.css' %}">
<head>
<div id='banner-container'>
<div id='banner'>
<h1 id='header'>MY APP</h1>
<i class="glyphicon glyphicon-cloud" style="font-size:60px;color:lightblue;text-shadow:2px 2px 4px #000000;"></i>
</div>
<div>
<body>
<div id='upload-container' >
<span><h1>Upload File !</h1></span>
<span><h2>Upload Here</h2></span>
<form method="post" enctype="multipart/form-data">
<div id='input'>
{% csrf_token %}
<input type="file" name="excel_file">
<div id='btn'><button type="submit">Upload File</button> </div>
</form>
<div>
</div>
</body>
{{ excel_data }}
</head>
</html>

Django provided us a message framework which allow you to attach messages and then you can render it on your template by using JavaScript or simply just use django template.
My favorite library to show message on my web application is toastr. You can go to the document page to see how you will integrate into your project.
On your views:
from django.contrib import messages
# ...
def upload(request):
if "GET" == request.method:
messages.error(request, "There's no file uploaded")
return render(request, 'uploadpage/upload.html', {})
# ...
Then on your template you can use it like so:
...
<head>
...
<link href="toastr.min.css" rel="stylesheet" />
</head>
<body>
...
<script src="toastr.min.js"></script>
{% if messages %}
<script>
toastr.options = {
"showDuration": "300",
"hideDuration": "1000",
"timeOut": "5000"
}
{% for message in messages %}
toastr.{{ message.tags }}("{{ message }}");
{% endfor %}
</script>
{% endif %}
</body>
message.tags: Using to match with the function of toastr, for example if you want to show an error by using messages.error(...) then the message.tags will be error, when your template rendered it turned to toastr.error("Your message here") and then you'll see the toast message on your browser.
Hope that helps!

There's two ways to go about it:
You can create a client-side check that prevents the form from being sent with Javascript. That is not Django-specific and you'll have no trouble finding examples.
You catch the fact that no file was sent and set an extra flag for the upload.html template. Untested example code:
message = None
data = None
if request.FILES:
data = # process file
message = "Upload successful!"
else:
message = "Please upload a file!"
return render(request, 'upload.html', {"data": data, "message": message})
Then you can show the message in the template:
{% if message %}
<div class="message">{{message}}</div>
{% endif %}

Related

Nonetype error while saving file with dropzone js in Django

I am trying to save documents/images into my SQL database using dropzone.js ,for my web app made using Django
HTML FILE:
{% extends 'Main/logged_base_expert.html' %}
{% block content %}
{% load static %}
<head>
<script src="https://rawgit.com/enyo/dropzone/master/dist/dropzone.js"></script>
<link rel="stylesheet" href="https://rawgit.com/enyo/dropzone/master/dist/dropzone.css">
</head>
<body>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-select/1.7.5/css/bootstrap-select.min.css" media="screen">
<form action="{% url 'document_upload' %}" method = "POST" class="dropzone dz" role="form" enctype="multipart/form-data">
{% csrf_token %}
<div class="fallback">
<input name="file" type="file" id="file" multiple >
</div>
<button type="submit" class="form-control" id="cf-submit" name="submit">Submit</button>
</form>
<!-- client section end -->
<!-- footer section start -->
{% endblock %}
views.py :
def document_upload(request):
c = main.objects.get(username = request.session['username'])
unq_id = c.unq_id
print("overall")
if request.method == 'POST':
images = request.FILES.get('file')
print("image")
fs = FileSystemStorage()
filename = fs.save(images.name, images)
Doc1 = Doc(user_id = unq_id, upload = images)
Doc1.save()
return render(request, 'Main/document_upload.html')
models.py:
class Doc(models.Model):
unq_id = models.AutoField(primary_key=True)
user_id = models.BigIntegerField()
upload = models.ImageField(upload_to= 'expert/images')
def __str__(self):
return str(self.unq_id)
The issue I keep facing is that , i keep getting the error
AttributeError at /document_upload/
'NoneType' object has no attribute 'name'
It occurs in the following line, in views.py:
filename = fs.save(images.name, images)
Additional Info : the relative path of the image seems to be storing just perfectly in the database, but since i keep getting the error , i am unable to redirect the app to another page
you're getting AttributeError because this couldn't be found in your request - request.FILES.get('file'). Print request.FILES and see what you have in there.

redirect() / render_template() enters the route once again when called from the same route in Flask

I have created a wishlist page which lists the items wishlisted by a user. The user can remove any item from the wishlist by clicking on the Remove button. On clicking the remove button, the HTML <form> submits it to the back-end application. The back-end application then removes the item from the wishlist database and redirects back to the same page using return redirect(url_for('wishlist')).
The problem I am facing is that if the user goes back after removing the item from the wishlist he has to go back twice to reach the page from where the user came from. This is caused due to the redirect that I am performing after removing the item which is necessary to show the updated wishlist.
I have also tried render_template() instead of redirect() but it is also causing the same problem.
Code for back-end:
#app.route('/wishlist/',methods=['GET','POST'])
#login_required
def wishlist():
userid=current_user.get_id()
if request.method=='POST':
toRemove=request.form['remove']
deleteWish=session.query(Wishlist).filter_by(userId=userid,productId=toRemove).one()
session.delete(deleteWish)
session.commit()
return redirect(url_for('wishlist'))
subquery=session.query(Wishlist.productId).filter(Wishlist.userId==userid).subquery()
wishes=session.query(Products).filter(Products.id.in_(subquery))
return render_template("wishlist.html",wishes=wishes)
HTML:
<html>
<body>
{% for wish in wishes %}
<img src={{wish.image_path}} width="150" height="200">
</br>
{{wish.material}}
{{wish.productType}}
</br>
{{wish.price}}
</br>
<form action="{{url_for('wishlist')}}" method="POST" target="_self">
<button name="remove" type="submit" value="{{wish.id}}">Remove</button>
</form>
</br>
{% endfor %}
</body>
</html>
Please suggest me a way to prevent this.
You may want to create a different end point for deleting wishes. This endpoint then redirects to your wish list once deletion is done.
FLASK
#app.route('/wishlist/',methods=['GET','POST'])
#login_required
userid=current_user.get_id()
def wishlist():
subquery=session.query(Wishlist.productId).filter(Wishlist.userId==userid).subquery()
wishes=session.query(Products).filter(Products.id.in_(subquery))
return render_template("wishlist.html")
#app.route('/deletewish', methods = ['GET', 'POST']
def deletewish():
if request.method=='POST':
toRemove=request.form['wish_delete_id']
deleteWish=...
session.delete(deleteWish)
session.commit()
return redirect(url_for('wishlist'))
HTML
<html>
<body>
{% for wish in wishes %}
<img src={{wish.image_path}} width="150" height="200">
</br>
{{wish.material}}
{{wish.productType}}
</br>
{{wish.price}}
</br>
<form method="POST" target="_self">
<button class="remove_wish" type="submit" value={{wish.id}}>Remove</button>
</form>
</br>
{% endfor %}`
<script src='path_to_jquery.js'></script>
<script src='path_to_deletewish.js'></script>
</body>
</html>
JS //deletewish.js content
<script>
$(document).ready(function() {
$('.remove_wish').click(function (event) {
event.preventDefault();
$.ajax({
data : {
wish_delete_id : $(this).val();
},
type : 'POST',
url : '/deletewish',
success: function (data) {
location.reload();
},
error: function (e) {
alert('something went wrong')
}
});
});
})
</script>

Python - Flask - MatPlotLib

I am trying to embed a plot from a function that I pass into my flask application.
The below does everything fine but launches the plot outside of the browser.
I've read a lot about IO and all but I'm confused on how/where to handle that.
The function runBB() in app.py returns plt.show(). After being passed symbol which is retrieved from the form. Should I be passing fig instead to do the IO calculations in app.py or should I do that in the file that holds the function?
I don't want to save the file anywhere as I want this to be dynamic from the stand point of multiple not overwriting the saved file which is what IO gets around I believe.
app.py
#Dashboard page
#app.route("/dashboard", methods = ["POST", "GET"])
def dashboard():
if person["is_logged_in"] == True:
return render_template("dashboard.html", email = person["email"], name = person["name"])
else:
return redirect(url_for('login'))
#app.route("/form", methods = ["POST", "GET"])
def form():
if request.method == "POST":
symbol = request.form["symbol"]
bytes_obj = runBB(symbol)
return render_template("dashboard.html", symbol=bytes_obj)
dashboard.html
{% extends "layout.html" %}
{% block content %}
<head>
<title>Welcome</title>
<link rel="stylesheet" type="text/css" href="{{url_for('static', filename = 'dashboard.css')}}">
<link rel="stylesheet" type="text/css" href="../static/dashboard.css">
</head>
<body>
<div class="main">
<h1 class="name">Hi, {{name}}</h1>
<form action="{{ url_for('form') }}" method ="POST">
<p><input placeholder="Enter your symbol" type="text" name="symbol"></p>
<p><input type="submit" value="Run Report" class="btn-default"></p>
</form>
<P>{{bytes_obj}}</P>
<!-- <hr style="width: 30%"> -->
<h3 class="email">{{email}}</h3>
</div>
</body>
{% endblock %}
Can someone point me in the right direction?
I've tried to comprehend other similar stackoverflow questions and can't seem to grasp this or put it together.
Please refer to this link to learn how to do this using:
import io
import base64
https://gitlab.com/snippets/1924163

Flask and WTForms with custom validation

I am creating an online form with Python, to send an online form. The form consists of a mixture of free input fields, and standard options. What it does now is convert the input to a mail, and send it. That's great, but I would like to build in a functionality that checks the input first. I need the length of two different inputfields to be of the size. So if someone enters 4 products and only 3 quantities, I want it to return a warning that these amounts differ.
base.py:
from flask import *
from wtforms import *
import yagmail
yag = yagmail.SMTP('email', 'pass')
# App config.
DEBUG = True
app = Flask(__name__)
app.config.from_object(__name__)
app.config['SECRET_KEY'] = 'key'
class ReusableForm(Form):
naam = TextField('Name:', validators=[validators.required()])
#app.route("/", methods=['GET', 'POST'])
def index():
form = ReusableForm(request.form)
print form.errors
if request.method == 'POST':
# Process all input
naam=request.form['naam']
productcodes=request.form['productcodes']
productquantity=request.form['productquantity']
# Convert SKU & EAN input to list of entries
productcodes = [int(i) for i in productcodes.strip('{}').split('\n')]
productquantity = [int(i) for i in productquantity.strip('{}').split('\n')]
# tried this; didn't work
# if len(productcodes) != len(productquantity):
# flash('Unequal inputs')
if form.validate():
# Comment when form is validates
flash('Order succesvol: ' + naam)
(send mail)
else:
flash('Error: All the form fields are required. ')
return render_template('hello.html', form=form)
if __name__ == "__main__":
app.run()
hello.html:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
<title>Form</title>
<link rel="stylesheet" media="screen" href ="static/css/bootstrap.min.css">
<link rel="stylesheet" href="static/css/bootstrap-theme.min.css">
<meta name="viewport" content = "width=device-width, initial-scale=1.0">
</head>
<body>
<div class="container">
<h2>Form</h2>
<form action="" method="post" role="form">
{{ form.csrf }}
<div class="form-group">
<label for="naam">Naam:</label>
<select name="naam" class="selectpicker form-control">
<option value="Jack">Jack</option>
<option value="John">John</option>
</select>
<br>
<label for="productcodes">SKU-codes:</label>
<textarea class="form-control" id="productcodes" name="productcodes"></textarea>
<br>
<textarea class="form-control" id="productquantity" name="productquantity"></textarea>
<br>
</div>
<button type="submit" class="btn btn-success">Send</button>
</form>
<br>
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
{% for message in messages %}
{% if "Error" not in message[1]: %}
<div class="alert alert-info">
<strong>Success! </strong> {{ message[1] }}
</div>
{% endif %}
{% if "Error" in message[1]: %}
<div class="alert alert-warning">
{{ message[1] }}
</div>
{% endif %}
{% endfor %}
{% endif %}
{% endwith %}
</div>
<br>
</div>
</div>
</body>
</html>
So what i would like to have is that upon the first click, the check is executed. If the size of the input of productcodes is smaller than the size of productquantity, the user has to edit the inputs. If not, the user has to confirm and is able to send the mail.
The lines that are commented out in base.py didn't work.
Thanks in advance!
Quick and Dirty
Based on the commented-out code, in your original attempt there was nothing stopping the form from validating and submitting. Your if len(productcodes) != len(productquantity) just flashed a message, which only shows on the next page load, but doesn't stop validation of the form. form.validate() can only validate the fields you tell it about.
Change the check to be something like:
if len(productcodes) != len(productquantity) and form.validate():
That way your basic check will bounce to the failed validation code path.
The "Right" Way
Keep in mind, even if that works, you're still basing your check on parsing text from a <textarea>, with little to enforce the format. Any unexpected input is likely to break it.
I'd strongly recommend you rethink your form to be a bit more in line with how WTForms and Flask work. Each form field should map directly to an attribute of your Form subclass. That way you can use the built-in validators, and not have to create your own. You should probably look at using something other than a raw <textarea> as well. It may be a bit more work up-front, but it will make it a lot easier for you and your users down the road.

Submitting contents of file to a Django view in HTML

I'm working with Django v1.4 (Python v.2.7.3) and I'm trying to build a proofchecking application. My proofchecker has an "Examples" page with a list of links to example proofs for the user, and these are rendered on the screen one after the other. A small sample is shown below:
These example files are saved at MEDIA_ROOT on the server, and what I want is a way to make it so that clicking the link will pass the contents of the file in a POST message to a particular view. That view already has code designed to handle the user uploading a proof file directly from their file system, so essentially what I want to do is make it so that the examples.html template (shown below) passes the same kind of information except for files already stored on the server.
The code for examples.html is:
{% load staticfiles %}
<html>
<head>
<title>Anaconda</title>
<style>
body
{
overflow-y: scroll;
}
</style>
</head>
<body>
<center>
<img src="{% static "AnacondaTitleText.png" %}" alt="Title" height="40%" width="40%"/>
<div align="left" style="width:800px;">
<h2>Anaconda Examples</h2>
<p>You can click the button beside each example on this page to load it into the proof window.
{% if examples %}
The following examples are included with Anaconda:</p>
<br>
{% for example in examples %}
<p>{{ example.exampleFile.name|cut:"./" }}: link</p>
<br>
{% endfor %}
{% else %}
There are no examples currently included with Anaconda.</p>
{% endif %}
</div>
</center>
</body>
</html>
The "a href..." part will need to be changed because currently, clicking it will bring up a "Save file" dialog which is not what I want.
In my server's views.py file, I have the following code capable of handling uploaded files:
def proof(request):
if request.method == 'POST':
defaultText = request.FILES['docfile'].read()
proofText = ProofForm(request.POST)
else:
defaultText = ""
proofText = ProofForm()
c = {}
c.update(csrf(request))
c['form'] = proofText
c['default_text'] = defaultText
return render_to_response('server/proof.html', c)
I suppose what I want is a way to do the following:
The user clicks the link next to a particular example proof
All the necessary information gets loaded into request.FILES
This gets sent to the server as a POST request to proof(request)
proof(request) treats it like a regular uploaded file and reads the file contents
My models.py file looks like this:
from django.db import models
class Example(models.Model):
exampleFile = models.FileField(upload_to='.')
I'd be happy to provide additional information if necessary.
I found a solution to this, but I suspect it's a bit hacky.
Inside examples.html I include the following:
{% for example in examples %}
<form name="example" action="/server/proof" method="post">
{% csrf_token %}
<p>{{ example.exampleFile.name|cut:"./" }}</p>
<input type="hidden" name="example" value="{{ example.exampleFile.name|cut:"./" }}">
<input type="submit" value="Select">
</form>
<br>
{% endfor %}
I associate the name of the file with a hidden input element, and then in views.py I have the following code:
def proof(request):
if request.method == 'POST':
print "DATA"
print str(request.POST)
try:
defaultText = request.FILES['docfile'].read()
except:
examplePath = request.POST.get('example')
with open(join(MEDIA_DIR, examplePath), 'r') as f:
defaultText = f.read()
proofText = ProofForm(request.POST)
else:
defaultText = ""
proofText = ProofForm()
c = {}
c.update(csrf(request))
c['form'] = proofText
c['default_text'] = defaultText
return render_to_response('server/proof.html', c)
It's patchwork, but it does what I want. Hopefully I'll have time to improve the code at some later time.

Categories

Resources