How do I keep the value of a FileField after submission? - flask

My app looks as follows:
from flask import Flask, render_template, request
from flask_wtf import FlaskForm
from wtforms import IntegerField, FileField
app = Flask(__name__)
app.secret_key = 'secret'
class MyForm(FlaskForm):
myFile = FileField('File')
myInt = IntegerField('Number')
#app.route('/', methods = ['POST','GET'])
def home():
form = MyForm()
if request.method == 'GET':
return render_template('index.html', form=form)
else:
myFile = form.myFile
myInt = form.myInt
print("The file is {} and the int is {}".format(myFile.data, myInt.data))
return render_template('index.html', form=form)
if __name__ == "__main__":
app.run(debug=True)
and my HTML file is:
<html>
<body>
<form action="/" method="POST" enctype="multipart/form-data">
{{ form.myFile.label }} {{ form.myFile }}<br>
{{ form.myInt.label }} {{ form.myInt }}<br>
<input type="submit" value="Submit" />
</form>
</body>
</html>
If I select a file, enter a 4 in the IntegerField and press Submit, I get the following print:
The file is <FileStorage: 'file.txt' ('text/plain')> and the int is 4
After the page reloads, I still see the 4 I entered, but I no longer see the name of the file next to the Browse button. Unsurprisingly, if I click Submit again, I get:
The file is <FileStorage: '' ('application/octet-stream')> and the int is 4
How can I pass on the file name after hitting the button? Even if it's not displayed to the user, I would like the value to remain stored in the form.
I know I can save the file, but I would like to avoid that, and I don't understand why FileField's behavior is so different from that of IntegerField.

Related

How to make the Flask app working in real time with two pages one page for data entry and another for display concurrently?

I have a flask app to enter data by a person through a page and display through another page to all. This is done with following as app.py
from flask import Flask, render_template, request, jsonify, url_for, redirect
app = Flask(__name__)
#app.route('/', methods = ['GET', 'POST'])
def index():
if request.method == 'POST':
date = request.form.get('date')
return redirect(url_for('booking', date=date))
return render_template('index.html')
#app.route('/display')
def booking():
date = request.args.get('date', None)
return render_template('display.html', date=date)
if __name__ == '__main__':
app.run(debug=True)
with index.html:
<html>
<head></head>
<body>
<h3>Home page</h3>
<form action="/" method="post">
<label for="date">Date: </label>
<input type="date" id="date" name="date">
<input type="submit" value="Submit">
</form>
</body>
</html>
with display.html:
<html>
<head></head>
<body>
<h3>Date display</h3>
<p>
Seleted date: {{ date }}
</p>
</body>
</html>
Now what happens is upon selecting a date in index.html, upon submitting, users are taken to display.html with date value in url and displayed.
But what I want is the display page should independently display the data whatever is entered through index.html dynamically. My app requires data entry will be done by one person and display will be seen by rest all in real time.
Is this possible?

Form not valid in django

Hi I am learning Django for a project and I am trying to upload a file along with a on option in dropdown list through a form using POST.
Here are my files:
views.py
from __future__ import unicode_literals
from django.shortcuts import render
from django.http import HttpResponse
from .forms import UploadFileForm
# function to handle an uploaded file.
from save_uploaded_file import handle_uploaded_file
def index(request):
if request.method == 'POST':
form = UploadFileForm(request.POST, request.FILES)
if form.is_valid():
handle_uploaded_file(request.FILES['file'])
return render(request, 'viewer_app/display_image.html')
else:
print('error')
return render(request, 'viewer_app/index.html', {'form': form})
else:
return render(request, 'viewer_app/index.html')
forms.py
from django import forms
class UploadFileForm(forms.Form):
file = forms.FileField()
displayType = forms.ChoiceField(widget=forms.Select(), required=True)
save_uploaded_file.py
def handle_uploaded_file(f):
with open('static/viewer_app/temp.exr', 'wb+') as recieved_exr:
for chunk in f.chunks():
recieved_exr.write(chunk)
index.html
<div id="formDiv" style="display:none;" class="form" >
<form method="post" enctype="multipart/form-data" class="form-style">
<label for="browse">Upload file</label>
<input type="file" value="Browse" id="brow" /><br></br>
<label for="display">Display type</label>
<select id="display-type" name="display">
<option id="RGB1" value="RGB1">RGB1</option>
<option id="RGB2" value="RGB2">RGB2</option>
<option id="RGB3" value="RGB3">RGB3</option>
<option id="RGB4" value="RGB4">RGB4</option>
</select><br></br>
<input type="submit" value="Show file" id="showimage"/><br></br>
{% csrf_token %}
</form>
</div>
So, after I run the server to display the page and I select the upload and click submit, it doesn't upload and save the file and in the terminal I see the "error" text in the terminal which is displayed due to from.is_valid() not being true.
I tried to add {{ form.errors }} {{ form.non_field_errors }} to the html file but I still don't know exactly what is that I am doing wrong.
Also I can see the absence of name attributes inside your <input> tag and a potentially wrong name attribute inside your <select> tag. You should put the exact name of the field inside your html 's name attribute for the form to get bounded. Same goes for the <select> tags.
Try putting:
<input type="file" value="Browse" id="brow" name="file"/>
and
<select id="display-type" name="displayType">
and probably your problem will be solved.
If I may suggest, why aren't you using the standard django template form tags, Such as {{ form.as_p }} ? These methods are quite handy and will also handle the errors successfully. You should try using them. check them here
EDIT
You haven't given any choices attribute to you ChoiceField in your forms.py. Make sure to give a attribute choices as a tuple to your ChoiceField. It can be done as:
#import ugettext_lazy
from django.utils.translation import ugettext_lazy as _
#inside your form class
class UploadFileForm(forms.Form):
CHOICES = (
('RGB1', _('RGB1')),
('RGB2', _('RGB2')),
('RGB3', _('RGB3')),
#And so on
)
#First option in the tuple is stored in db. Second is displayed in Forms.
displayType = forms.ChoiceField(widget=forms.Select(), required=True , choices = CHOICES)
file = forms.FileField()
EDIT 2:
Get the file url as below,
from django.contrib.staticfiles.templatetags.staticfiles import static
url = static('viewer_app/temp.exr')
#See if temp.exr exists prior to this. Otherwise create a file manually or through python.
Then open the file by supplying this url.
Again I would recommend you to check ModelForms. They'll completely eliminate the need to write files this way.
Hope it helps. Thanks.

DJANGO - Redirect to different page with form data

Hi i want to redirect to a destination page with the from data. For example when user fills a form the data inputted in the form, i want that to be outputted on the destination page
my codes are as follows:-
source page(experiment.html), I am unsure what the action should be for the form so please help me with it
<form action="{% url 'lazer.views.about_experiment' exp.link_name %}" method="POST">
{% csrf_token %}
<label>Researcher Name(s):<input type="text" name="researcher">
<lable>Study Summary<textarea rows="10" cols="50" placeholder="here you go" maxlength="500" class="form-control" name="study"></textarea>
<br>
<input type = "submit" value="Submit" class="btn btn-primary" />
</form>
destination page (about_experiment.html)
<h3>Holding page for {{ exp.name }}.</h3>
<h2> {{ form }} </h2>
views.py
from .forms import AboutHelp
from django.shortcuts import render
from django.http import HttpResponseRedirect
def about_experiment(request):
if request.method == 'POST':
form = AboutHelp(request.POST)
if form.is_valid():
researcher = form.cleaned_data['researcher']
study = form.cleaned_data['study']
else:
form = AboutHelp()
return render(request, 'about_experiment.html', {'form': form})`
forms.py
from django import forms
class AboutHelp(forms.Form):
researcher = forms.CharField(max_length=100)
study = forms.CharField(max_length=500)
urls.py
url(r'^about/(?P<ex_link_name>\w+)', lazer.views.about_experiment, name='lazer.views.about_experiment'),

Flask: form.validate_on_submit() throwing type error

Everytime I submit my form on '/signup' view, form.validate_on_submit() in my views.py throws the error below:
TypeError: __init__() takes from 1 to 2 positional arguments but 3 were given
The stack trace is pretty long and I don't see anything immediately obvious. I have no idea why it is doing this. I followed the Flask-WTF docs for validating forms.
EDIT: Here is the stack trace I am seeing.
views.py
from myapp import app
from flask import render_template, redirect
from forms import RegistrationForm
#app.route('/', methods=['POST', 'GET'])
#app.route('/signup', methods=['POST', 'GET'])
def signup():
form = RegistrationForm()
if form.validate_on_submit():
# Redirect to Dash Board
return redirect('/dashboard')
return render_template("signup.html", form=form)
#app.route('/login')
def login():
return "<h1>Login</h1>"
#app.route('/dashboard')
def dashboard():
return "<h1>Dashboard</h1>"
forms.py
from flask_wtf import FlaskForm
from wtforms import TextField, PasswordField
from wtforms.validators import InputRequired, Email, Length
class RegistrationForm(FlaskForm):
username = TextField('username', validators=[InputRequired(), Length(min=4, max=30)])
email = TextField('email', validators=[InputRequired(), Email, Length(max=25)])
password = PasswordField('password', validators=[InputRequired(), Length(min=8, max=80)])
class LoginForm(FlaskForm):
username = TextField('username', validators=[InputRequired(), Length(min=4, max=30)])
password = PasswordField('password', validators=[InputRequired(), Length(min=8, max=80)])
signup.html
{% extends "base.html" %}
{% block content %}
<h1>Sign Up</h1>
<form method="POST" action="/signup">
{{ form.hidden_tag() }}
<p>Username:</p>
{{ form.username() }}
<p>Email:</p>
{{ form.email() }}
<p>Password:</p>
{{ form.password() }}
<br/>
<br/>
<button type="Submit" value="submit" name="submit">Submit</button>
</form>
{% endblock %}
I figured it out! In forms.py, my RegistrationForm's email attribute should read:
email = TextField('email', validators=[InputRequired(), Email(), Length(max=25)])
I forgot the darn parenthesis for the Email parameter.

Django: Form Data Not Being Accessed to Store Data in Session

views.py
from textize.models import Textizer
from django.http import HttpResponseRedirect
from django.shortcuts import render_to_response
from django.core.context_processors import csrf
def index(request):
if request.method == 'POST':
form = Textizer(request.POST)
print "debug 0" # <---It's not reaching this point when I submit the data via the form
if form.is_valid(): #check to see if the input is valid
print "debug 1"
request.session['text'] = form.cleaned_data['to_textize'] #set the session key:value pair
return HttpResponseRedirect('/results') #redirect to the results page
else:
form = Textizer()
print "debug 2" # reaches here
c = {'form': form}
c.update(csrf(request))
return render_to_response('index.html', c)
def results(request):
text = request.session.get("text", "dummy")
c = {'text' : text}
return render_to_response('results.html', c)
index.html
<form action="/results" method="POST"> {% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Submit" />
</form>
results.html
<b>Input text: </b>{{ text }}
I'm trying to pass data from the 'index' page to the 'results' page. In this case, I'd like to display the string typed and submitted on the results page.
What's wrong with my form?
Also, am I forming the session key:value correctly?
from textize.models import Textizer
from django.http import HttpResponseRedirect
from django.shortcuts import render_to_response
from django.core.context_processors import csrf
def index(request):
form = Textizer(request.POST or None)
if request.method == 'POST':
print "debug 0"
if form.is_valid():
print "debug 1"
request.session['text'] = form.cleaned_data['to_textize']
c = {'form': form, 'text':request.session.get("text", "")}
c.update(csrf(request))
return render_to_response('index.html', c)
and then template index.html
<form action="" method="POST"> {% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Submit" />
</form>
result: {{ text }}
is enough to get this going.