Flask - Pre-populate Fields from a text file - python

I am looking for a simple way to pre-populate fields of a form with data from a text file.
Here are my imports:
#For Flask
from flask import Flask, render_template, flash, request, redirect, url_for, send_file
#For Web Form
from wtforms import Form, TextField, TextAreaField, validators, StringField, SubmitField, SelectField, RadioField, IntegerField, DateTimeField, DecimalField, FloatField
from werkzeug.utils import secure_filename
import re #For Regex
I have the following class for my form:
class MainForm(Form):
#Regex
reEmail_comp = re.compile('^.+#.+\..+$')
reJobname_comp = re.compile('[^\s]+[\w]+$')
#Form Fields
familyname = TextField('Family Name:',
validators=[validators.DataRequired()])
firstname = TextField('First Name:',
validators=[validators.DataRequired()])
email = TextField('Email:',
validators=[validators.DataRequired(),
validators.Length(min=3, max=51),
validators.Regexp(regex=reEmail_comp)])
# Copy List of Job Templates
list_job_name = list_job_templates
# Create List of Tuples
choices_list = list(zip(list_job_name,list_job_templates))
# Generate string to be evaluate as analyses Radiofield
radio_string = ("RadioField('Analysis Type:',"
"validators=[validators.DataRequired()],choices="+str(choices_list)+")")
# Attribute value to element as a variable
locals()["analyses"] = eval(radio_string)
jobname = TextField('Job Name:',
validators=[validators.DataRequired(),
validators.Length(min=3, max=100),
validators.Regexp(regex=reJobname_comp)])
jobdescription = TextAreaField('Job Description:',
validators=[validators.Optional(),
validators.Length(min=0, max=10000)])
And the following view:
#app.route('/home', methods=['GET', 'POST'])
def home():
form = MainForm(request.form)
print(form.errors)
if request.method == 'POST':
familyname=request.form['familyname']
firstname=request.form['firstname']
email=request.form['email']
analyses=request.form['analyses']
jobname=request.form['jobname']
jobdescription=request.form['jobdescription']
if jobdescription == "":
jobdescription = "No Description"
nextpage=request.form['submit'] #Get submission button value
if form.validate():
#Print Temp file
print("||".join(["Family Name={}".format(familyname),
"First Name={}".format(firstname),"E-Mail={}".format(email),
"Analyse={}".format(analyses),"Job Name={}".format(jobname),
"Job Description={}".format(jobdescription)]),
file=open(submission_dir+"temp1", "w"))
#Redirections following which button has been clicked
if nextpage == 'Continue to Submission':
return redirect(url_for('nextpage'))
else:
return redirect(url_for('nextbis'))
else:
flash('Error: Please make sure that all the fields are correctly filled.')
return render_template('home.html', form=form)
The user has access to the form and can fill it.
As you can see in the view there is a step during which all information are stored in a temporary "temp1.txt" file once the user submits its information.
What I would like to do is to give the user the option to go back to this form in case he would like to modify an information he previously submit.
And to simplify this, I would like the form to display the same fields, but this time, pre-populated with the information found in the temporary text file from the previous submission.
How can I do that in a simple way ?

If you created a form and populated it from request, then same can be done with file values.
form = MainForm(read_file_in_correct_format())
where read_file_in_correct_format method returns object {fieldname:value}. It would be probably better to save as JSON so you can easily parse it and support more complex structures.
All you have to do is correct initialize form based on request.method
#app.route('/home', methods=['GET', 'POST'])
def home():
if request.method == 'POST':
# so if form is submitted, populate it from request
form = MainForm(request.form)
...
else:
# if we are just viewing form, then try to load stored values from temp1.txt
form = MainForm(read_file_in_correct_format())

Related

Having trouble transforming a form IntegerField to an integer variable in order to use it at calculating date

TypeError: int() argument must be a string, a bytes-like object or a number, not 'IntegerField' HERE IS THE ERROR
from flask import Flask, render_template
from wtforms import IntegerField, SubmitField
from flask_wtf import FlaskForm
import datetime
app = Flask(__name__)
app.config['SECRET_KEY'] = 'alex'
class Calculator(FlaskForm):
Year = IntegerField('Year')
Month = IntegerField('Month')
Day = IntegerField('Day')
submit = SubmitField('Calculate')
tdy = datetime.date.today()
#app.route("/")
#app.route("/home")
def home():
return render_template('home.html')
#app.route("/about")
def about():
return render_template('about.html')
#app.route("/Calculator", methods=['GET', 'POST'])
def days():
form = Calculator()
return render_template('calculator.html', form=form)
#app.route('/HURRAY', methods=['GET'])
def ura():
form = Calculator()
y = int(form.Year)
m = int(form.Month)
d = int(form.Day)
till_bday = tdy - datetime.date(y, m, d)
return render_template('HURRAY.html', till_bday = till_bday)
if __name__ == '__main__':
app.run()
The idea of the whole app is the following: You have a form, enter YEAR MONTH AND DAY in a IntegerField used with WTFORMS and when you click SUBMIT on that page you are redirected to a page where your result is shown. This sounds simple until I realized i have no idea how to make my IntegerField data in an integer variable that i can calculate and pass through my HTML file....
As you pointed out, you don't want the form's field themselves, you want the data sent along with the post request.
On the "/calculator" page, when you click submit, a post request is sent to your server, with data containing the values of each field in your form. Now I'm not sure how it works in Flask, but you want to find:
Which route in your server this request has been sent to? (probably "/calculator" in this case, which seems to be the only route that accepts POST requests)
Within this route, how can you access the request sent to it, and the data sent with it?
I'd recommend you have a clear understanding of how these HTTP requests work first (GET, and POST mainly) and how and where they are sent/received in your application.

Flask redirects to wrong view when redirecting to index

I keep running into this strange issue that I can't seem to figure out a solution for. I cannot copy and show all of my code in it's entirety here, but I will try to outline the general structure of my flask app to present my issue.
(Let's ignore all of the content in the /static folder and my helper modules)
I have 3 main views, let's call them viewA, viewB, and index:
viewA.html
viewB.html
index.html
viewA and viewB both display two forms, but with different content (i.e. viewA displays form1 & form2, and viewB also displays form1 & form2).
A simplified version of my script code is as follows:
#imports
from flask import Flask, render_template, session, redirect, url_for, request
from flask_wtf import FlaskForm
#etc. etc.
app = Flask(__name__)
app.config['SECRET_KEY'] = 'blah blah blah'
manager = Manager(app)
bootstrap = Bootstrap(app)
moment = Moment(app)
class FormOne(FlaskForm):
sample_field = StringField('Sample Field:')
class FormTwo(FlaskForm):
other_field = StringField('Other Field:', validators=[Required()])
submit = SubmitField('Submit')
class UploadToA(FlaskForm):
content= StringField('Content to send to view A:', validators=[Required()])
submit = SubmitField('Submit')
class UploadToB(FlaskForm):
content= StringField('Content to send to view A:', validators=[Required()])
submit = SubmitField('Submit')
#app.route('/ViewA', methods=['GET', 'POST'])
def view_a():
"""
A lot of data manipulation
"""
form1 = FormOne()
form2 = FormTwo()
if request.method == 'GET':
"""
populate forms with content
"""
if request.method == 'POST':
if form2.validate_on_submit();
"""
clear session variables
"""
return redirect(url_for('index'), code=302)
return render_template('viewA.html', form1=form1, form2=form2)
#app.route('/ViewB', methods=['GET', 'POST'])
def view_b():
"""
A lot of data manipulation
"""
form1 = FormOne()
form2 = FormTwo()
if request.method == 'GET':
"""
populate forms with content
"""
if request.method == 'POST':
if form2.validate_on_submit();
"""
clear session variables
"""
return redirect(url_for('index'), code=302)
return render_template('viewB.html', form1=form1, form2=form2)
#app.route('/', methods=['GET', 'POST'])
def index():
"""
Some data manipulation
"""
formA = UploadToA()
formB = UploadToB()
if formA.validate_on_submit()':
"""
pull content from form A
create some session variables
"""
return redirect(url_for('view_a'))
if formB.validate_on_submit()':
"""
pull content from form B
create some session variables
"""
return redirect(url_for('view_b'))
return render_template('index.html', formA=formA, formB=formB)
if __name__ == '__main__':
manager.run()
Now the issue at hand I am having here is that for some strange reason when I'm in 'viewA.html' and I submit my form, I SHOULD be redirected back to 'index.html' but for some strange reason it redirects me to 'viewB.html'. Furthermore, the opposite also holds true: when i'm in 'viewB.html' and I submit my form, I SHOULD also be redirected back to 'index.html' but it redirects me to 'viewA.html'. Yet, if I am in either viewA or viewB, I have no issues of going back to the index view if I manually enter the url into my browser.
Any ideas as to why I might be running into this issue?
Thanks in advance :)
I have finally figured out the source of my problem. It turns out that in my 'viewA.html' template file, I had the following in my < form > tag:
<form class="form form-horizontal" method="post" role="form" action="{{url_for('index')}}">
And the problem all lies in that last part:
action="{{url_for('index')}}"
As a result, everytime I would submit form2 in viewA.html it would create a post request for my index page rather than a post request for the viewA.html page (which caused a redirect to the wrong view). Thus, by simply removing the action attribute (action="{{url_for('index')}}"), I was able to solve my problem!
Since the full code isn't here, I can't confirm this for sure, but what I think is happening is this:
You open form A
You submit form A
It sends a redirect to /index
It sends a redirect to /FormB
if formB.validate_on_submit():
return redirect(url_for('view_b'))
This is probably sending a redirect to View B. Try changing that last line to something like return something_else and seeing if it sends that after submitting form A.

Flask-uploads - I keep getting UploadNotAllowed error

I am new to python and programming in general.
I keep getting UploadNotAllowed error even though I set the form field validator to Optional(). My goal is to allow users the choice of uploading or not uploading a profile picture. All configurations work well when an image if selected to be uploaded.
Any help will be appreciated.
Here is the form field:
class SettingsForm(FlaskForm):
profile_pic = FileField('Profile Picture', validators= [Optional(), FileAllowed(images, 'Only images are allowed here')])
Here is my views.py:
if form.validate_on_submit():
filename = images.save(request.files['profile_pic'])
current_user.profile_pic = images.url(filename)
this is a slightly edited version from the docs (here). Which also give you some reminders about what your html file should contain.
from flask_wtf import FlaskForm
from flask_wtf.file import FileField, FileRequired
from werkzeug.utils import secure_filename
class PhotoForm(FlaskForm):
photo = FileField(validators=[Optional(), FileAllowed(images, 'Only images are allowed here')])
#app.route('/upload', methods=['GET', 'POST'])
def upload():
if form.validate_on_submit():
f = form.photo.data
filename = secure_filename(f.filename)
#you can replace this with wherever you want to save your images
f.save(os.path.join(
app.instance_path, 'photos', filename
))
current_user.profile_pic = images.url(filename)
return redirect(url_for('index'))
return render_template('upload.html', form=form)

Python Django - Access Response Headers In View

I am working on a web application which works with entities that all have their unique IDs.
I have a submit form for users to create these entities and this form is in several steps (i.e. view 1 redirects to view 2, etc... until the end of the submission process).
The first view will create the ID of the entity after form submission and I then need to use the ID of the instance created in the other views.
I do not want to pass this ID as a URL parameter to the other views as these will be POST and that means that users could easily manipulate these and create records in models for several IDs. I have managed to pass this ID to several views using the session parameters (request.session) but this is not ideal as this is permanent for the session. Current code below:
def view1(request):
if request.method == 'POST':
form = xxx_creation_form(request.POST)
if form.is_valid():
cleaned_form_data = form.cleaned_data
xxx_entry = Model.objects.create(
... object creation ...
)
request.session['xxx_id'] = xxx_entry.id
return HttpResponseRedirect(reverse('form_2'))
else:
form = xxx_creation_form()
return render(request, 'xxx_form.html', {'form': form})
def view2(request):
xxx_id = request.session['property_id']
if xxx_id == 'SET_BACK_BLANK':
return render(request, 'no_xxx_id.html')
if request.method == 'POST':
form = xxx_form2(request.POST)
if form.is_valid():
cleaned_form_data = form.cleaned_data
xxx_entry = Model.objects.create(
id = xxx_id, #use the id created in step 1
... rest of object creation ...
)
request.session['xxx_id'] = 'SET_BACK_BLANK' #to avoid the misuse during other user interactions.
return HttpResponseRedirect(reverse('thanks'))
else:
form = xxx_form2()
return render(request, 'xxx_form2.html', {'form': form})
Ideally, I would like to pass this ID parameter in the headers of the response as this will avoid having the ID as a session parameter. So I have amended the code to the below:
def view1(request):
if request.method == 'POST':
form = xxx_creation_form(request.POST)
if form.is_valid():
cleaned_form_data = form.cleaned_data
xxx_entry = Model.objects.create(
... object creation ...
)
response = HttpResponseRedirect(reverse('form_2'))
response['xxx_id'] = xxx_entry.id
return response
else:
form = xxx_creation_form()
return render(request, 'xxx_form.html', {'form': form})
def view2(request):
xxx_id = HttpResponseRedirect(request).__getitem__('xxx_id')
if request.method == 'POST':
form = xxx_form2(request.POST)
if form.is_valid():
cleaned_form_data = form.cleaned_data
xxx_entry = Model.objects.create(
id = xxx_id, #use the id created in step 1
... rest of object creation ...
)
return HttpResponseRedirect(reverse('thanks'))
else:
form = xxx_form2()
return render(request, 'xxx_form2.html', {'form': form})
However the above does not work and the error message seems to indicate that there is no 'xxx_id' in the response header.
It would be great if anyone could let me know how to access a response's header in a view as it seems that we cannot amend the request's headers.
Thanks.
What you're asking doesn't really make sense. The response is what is sent from Django to the browser, it is not what the browser sends to Django. That's a completely separate request; in the case of a redirect, your response is simply an instruction to the browser to make that request to a new URL.
The correct thing to do is to use the session, as you are doing. If you are worried about the value being persisted, then pop it out of the session when you use it:
xxx_id = request.session.pop('property_id')

How to pass a "WTF object" in Flask

I am using flask to develop a website and now i encountered a problem.
I am thinking whether I can pass a "WTF form" object in flask.
Like,
#app.route('/')
#app.route('/index')
#login_required
def index():
user = flask.g.user
form = PostForm()
return flask.render_template("index.html",
title="Home",
user=user,
form = form)
This form, an instance of PostForm, actually will be processed by the following code:
#app.route('/note/<int:id>', methods=['POST'])
def note(id):
form = ?(how to get this form?)?
if form.validate_on_submit():
print id
content = form.body.data
currentTime = time.strftime('%Y-%m-%d', time.localtime(time.time()) )
user_id = id
return flask.redirect(flask.url_for('login'))
return flask.redirect( flask.request.args.get('next') or
flask.url_for('index') )
In the template, I set the action to be "/note/1", so it will forward to this address. But the question, how can I get the form created in the function index?
I have tried to use flask.g (Obviously, it does not work because it's another request). And I also tried to use global variable. It failed, either.
Could anyone give me a solution or any advice?
Thank you in advance!
You simply need to construct a new version of PostForm in your note route and use the posted data in request.form:
from flask import request
#app.route('/note/<int:id>', methods=['POST'])
def note(id):
form = PostForm(request.form)
# or, if you are using Flask-WTF
# you can do
# form = PostForm()
# and Flask-WTF will automatically pull from request.form

Categories

Resources