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.
Related
I am creating a website server program with Flask and Python, I am relatively new to Flask. I have created a html form with a Select tag (drop down menu, single select). You can see the code below.
selectmenu.html
<!doctype html>
<html>
<body>
<form id="form" method="POST" class="selectform">
<select method="GET" size=5>
{% for x in options %}
<option value="{{x}}" SELECTED>{{x}}</option>
{% endfor %}
</select>
<input type="submit" name="go"></input>
</form>
</body>
</html>
The Python code for Flask:
app.py
#app.route('/selectmenu',methods=(['GET','POST']))
def selmenu():
optionfiles = os.listdir('options')
form = selectform()
if request.method == 'POST':
selectedoption = form.selectedoption.data
print(selectedoption)
return redirect('/')
return render_template('selectmenu.html',options=optionfiles,form=form)
And finally the Python WTForms classes code forms.py:
class selectform(FlaskForm):
optionfiles = os.listdir('userdata')
print(optionfiles)
selectedoption = SelectField('selectdropdown', choices=optionfiles,validators=[DataRequired()])
submitbutton = SubmitField('submit',validators=[DataRequired()])
But when the program called form.selectoption.data it returns None every time.
I have tried many tutorials, and Stackoverflow answers, but still can't get data from the form that isn't None.
NOTE: As you can tell I have uploaded snippets only of the code, as the actual files are all a lot larger! If you need any other bits of the code, e.g: the imports, then please ask, but I am pretty sure the error isn't the imports, for example! Otherwise I would receive an ImportError.
Thank you in advance for your help!
After finding this Stackoverflow post I found the answer.
I needed to use give the <select> tag a name, like this: name="optionsbox" in the html file.
Then do request.form.get("optionsbox") in the app.py file, instead of form.selectedoption.data. No changes needed in the forms.py file.
It seems that you are using Flask-WTF (FlaskForm) but the way you write the html part is not compliant with it.
To confirm you can replace selectmenu.html by :
<!doctype html>
<html>
<body>
<form method="POST">
{{ form.selectedoption() }}
{{ form.csrf_token }}
{{ form.submitbutton(class_='btn btn-primary') }}
</form>
</body>
</html>
If it is the case you might have a look at https://flask-wtf.readthedocs.io/en/stable/quickstart.html and https://flask.palletsprojects.com/en/1.1.x/patterns/wtforms/#forms-in-templates (for the easy template).
(And return render_template('selectmenu.html',options=optionfiles,form=form) can be replaced by return render_template('selectmenu.html',form=form) )
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 %}
I'm building a Netflix like website for my Devops course. I made a Python list of dictionaries (Mockfilms) to define my films, and want to populate a database (Ratings) with reviews in preparation for sending data in the format :filmid: :userid: :rating: to a recommendation engine.
My index page is a list of film images with a link to a review form under each one. I want each review form to appear on a different url (/review/ID where ID is saved in mockfilms as oid). In order to do this I want to access mockfilms.oid, then pass it to the view function to make the url for the form. Once the form is complete I then want to add this ID to the Ratings database. Here is what I have so far:
Index:
{% extends "base.html" %}
{% block content %}
<h1>Hello, {{ current_user.username }}! Welcome to our extensive video library:</h1>
{% for film in mockfilms %}
{% set ID = film.oid %}
<div>
<a href = {{ film.video }}>
<img src = {{ film.image }} alt = "doh" style = "width:200px;height:200px;border:0;">
</a>
</div>
<div>
">Leave a review here!
{% endfor %}
{% endblock %}
Route:
#app.route('/review/<ID>', methods = ['GET', 'POST'])
#login_required
def review(ID):
form = ReviewForm()
if form.validate_on_submit():
review = Ratings(User_id = current_user.id, Score_given = form.score.data, Film_id = ID)
db.session.add(review)
db.session.commit()
flash('Thanks for your review')
return redirect(url_for('index'))
return render_template('review.html', title='Review Page', form=form)
The following error is what I get when I run it:
File "/home/jc/Desktop/Lokal/DevopsAssig/microblog/Kilfinnan/lib/python3.5/site-packages/werkzeug/routing.py", line 1768, in build
raise BuildError(endpoint, values, method, self)
werkzeug.routing.BuildError: Could not build url for endpoint 'review'. Did you forget to specify values ['ID']?
From this I assume that the issue is with the ID variable within this template. My searchings and learnings led me to believe that {% set %} in the index template would let me declare the ID variable and then use it in the dynamic.
Try this:
{% block content %}
<h1>
Hello, {{ current_user.username }}!
Welcome to our extensive video library:
</h1>
{% for film in mockfilms %}
<div>
<a href="{{ film.video }}">
<img src="{{ film.image }}" alt="doh" style="width:200px;height:200px;border:0;" />
</a>
</div>
<div>
<a href="{{ url_for('review', ID=film.oid) }}">
Leave a review here!
</a>
</div>
{% endfor %}
{% endblock %}
Ultimately your solution was quite close, but it is not necessary to use the Jinja set command when you need to pass the variable into url_for() function using the keyword for the parameter. You could still do it using {% set ID = film.oid %} but it would be a bit superfluous.
Try to provide key=value arguments into your url_for function.
Something like this
">Leave a review here!
Also Flask have a great documentation, Flask docs
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.
I'm not looking for exact code, but rather some guidance on my problem. On one of my pages, I want to have a small section that will tell the user the disk space left on the server and I want this information to update every 30 seconds. I have the code already written to get the disk information. However, I am unsure on how to display that information and have it update. This is what I have, just to give you a visual:
The HTML I'm working with:
{% extends "Checklist/base.html" %}
{% block main_content %}
<form action="{% url 'Checklist:run' %}" method="post"> {% csrf_token %}
<input type="submit" name="submit_button" value="Run">
</form>
<label for="disk_space">Disk Space: </label>
<input type="text" name="disk_space" value="{{ disk_space }}" id="disk_space">
{% endblock %}
The Views Function for the HTML:
def submit(request): #start scrtipt
submit.preProcess = subprocess.Popen(['chmod', '+x /home/psvuser/Desktop/test.sh'])
submit.process = subprocess.Popen(['/home/psvuser/Desktop/test.sh'])
global sem
sem = True
disk_space = request.get('disk_space', '')
disk_space = diskSpace()
start_time= int(round(time.time()))
f = open("/home/psvuser/Desktop/writer.txt", "a")
f.write(str(start_time))
f.write(", ")
return render(request, 'Checklist/stop.html')
I have an idea on how to periodically refresh the page, however, I don't know how to display 'disk_space' onto the html.
Since you want to do regular asynch calls to your server while a page is already loaded, I would use AJAX to solve this problem.
I'd usually do something like this
def your_django_view(request):
# magic
server_data = '98% left!'
response_data['data'] = server_data
return HttpResponse(json.dumps(response_data), content_type="application/json")
Then in your view, use some jquery (or javascript..whatever) to deal with all of the data
$.ajax({type: 'POST',
url: '/function_url/',
data: {
},
success: function(response_data) {
var server_mem = response_data['data']
$('#disk_space').val(server_mem);
})
Use this return statement at the end of your submit view:
return render_to_response('Checklist/stop.html',
{'disk_space':disk_space},
context_instance=RequestContext(request))
The second argument of render_to_response is a dictionary of {'key':value, 'key2':value2} you can use in the template.