Django render form with data when not validated - python

I wrote this contact form with some help from the Django docs:
def contact(request):
if request.method == 'POST':
form = ContactForm(request.POST)
if form.is_valid():
name = form.cleaned_data['name']
subject = form.cleaned_data['subject']
message = form.cleaned_data['message']
sender = form.cleaned_data['sender']
cc_myself = form.cleaned_data['cc_myself']
recipients = ['name#e-mail.com']
if cc_myself:
recipients.append(sender)
m = '%s\n\n-----------------\n%s' % (message, name)
from django.core.mail import send_mail
send_mail(subject, m, sender, recipients)
return HttpResponseRedirect('/kontakt/tack/')
else:
form = ContactForm()
return render(request, 'contact/index.html', {
'form': form,
})
The problem is that if the form doesn't validate it returns an empty form. That could be a big annoyance for someone that have written a long message but forgot to put his name in or something.
I've fixed that by change the the code below to last else: to:
try:
form = ContactForm(form.cleaned_data)
except UnboundLocalError:
form = ContactForm()
I needed to try-statement for the first rendering of the page when no form.cleaned_data yet exist. This works, but it seems like a rather ugly hack.
Is there some standard way to use the text from previous fill in when re-rendering the form without my ugly try-except solution?
Template on request
{% block content %}
<div id="contact-generic-container">
<p>Lorem ipsum (not really, but ain't relevant).</p>
</div> <!-- #contact-generic-container -->
<div id="contact-form" class="clearfix">
<br/>
<h2>Kontakta oss!</h2>
<form action="/kontakt/" method="post">
{{ form.non_field_errors }}
<fieldset class="text">
{% csrf_token %}
<div class="field-wrapper">
<label for="id_name">Namn:</label>
{{ form.name }}
{{ form.name.errors }}
</div>
<div class="field-wrapper">
<label for="id_sender">E-post:</label>
{{ form.sender }}
{{ form.sender.errors }}
</div>
<div class="field-wrapper">
<label for="id_subject">Ämne:</label>
{{ form.subject }}
{{ form.subject.errors }}
</div>
</fieldset>
<fieldset class="text">
<div class="field-wrapper">
<label for="id_message">Meddelande:</label><br/>
{{ form.message }}
{{ form.message.errors }}
</div>
<div class="field-wrapper">
<label for="id_cc_myself">Kopia till mig själv:</label>
{{ form.cc_myself }}
{{ form.cc_myself.errors }}
</div>
<input type="submit" value="Skicka" />
<fieldset class="text">
</form>
</div>
{% endblock %}

Here's a simple way to always either init the form with the POST data, or nothing:
def contact(request):
form = ContactForm(request.POST or None)
if request.POST and form.is_valid():
Then remove your else block.
The form will always have data in it, and won't throw an error if it's just a GET.
Since I'm using some Python trickery, allow me to explain.
Python supports and/or operator in expressions.
When evaluting an or python will stop when the first value returns a truthy value.
When evaluating an and, Python will stop if the first condition returns false.
So, when we init the form, we ask Python to assign either the value of request.POST (if it's true), or None, if request.POST is false (which it will be if it's empty). This inits the form with the values from request.POST correctly when there's a POST, but None otherwise.
In our if statement, if request.POST is false (which it will be if request.POST is empty), Python stops there and never calls form.is_valid(), so we don't have to worry about validation running if the POST isn't present.
Neat huh?

Related

How to dynamically add input fields in form for FieldList [Flask]?

I'm designing a website with a form that allows the user to input stuff and add more input fields, it sort of works like those inputfields that you enter your skills with, soi you can add more skills if you have more. But I'm struggling to display more than one input fields with FieldList as it only shows the number of min_entries I set it, even when I copy and paste the html code it only accepts the first one.
forms.py (please focus on the contents for now)
class ExerciseForm(FlaskForm):
exercise = StringField(validators=[DataRequired()])
testcase = StringField(validators=[DataRequired()])
class AddTopicForm(FlaskForm):
topicname = StringField('Topic Name', validators=[DataRequired(), Length(min=2, max=100)])
contents = FieldList(StringField(validators=[DataRequired()]), min_entries=1, max_entries=20)
exercises = FieldList(FormField(ExerciseForm), min_entries=0, max_entries=10)
submit = SubmitField('Add Topic')
html (I have no idea how to add one, and how to make it work)
<div class="addtopicform">
<form method="POST" action="">
{{ form.hidden_tag() }}
<fieldset>
<div class="form-group">
{{ form.topicname }}
</div>
<div class="form-group">
{{ form.contents }}
</div>
<button type="button" onclick="addsection()">Add Section</button>
<script>
</script>
<div class="form-group">
{% for field in form.exercises %}
{{ field }}
{% endfor %}
</div>
<div class="form-group form-btn">
{{ form.submit(class="btn") }}
</div>
</fieldset>
</form>
</div>
website.py (The best result is one data in contents, but obviously I want more)
#app.route("/addtopic/", methods=['GET', 'POST'])
def addtopic():
if 'AccountID' in session:
form = AddTopicForm()
if session["Type"] == "teacher":
if request.method == 'POST' and form.validate_on_submit():
print("success")
topicname = request.form['topicname']
contents = form.contents.data
exercises = form.exercises.data
print(topicname)
print(contents)
print(exercises)
return render_template('course.html', user=session['Name'], acctype=session["Type"])
else:
return render_template('addtopic.html', user=session['Name'], acctype=session["Type"], form=form)
else:
flash("Only teachers can access to this page", "failure")
return render_template('course.html', user=session['Name'], acctype=session["Type"])
else:
flash('An account is needed to access courses', 'failure')
return redirect(url_for('login'))
PLease help me, I couldnt find any useful resources online as all of them assumed I already have a list or is using the input element instead of flask element, which doesnt work at all with flask forms

Flask hidden input doesn't get set in template

So I'm trying to pass a value from a Jinja2 template back to my Python code. I'm trying to do this with a hidden input. My form class is this:
class TrueOrFalseForm(flask_wtf.FlaskForm):
choice = RadioField(choices=[('True', 'TRUE'), ('False', 'FALSE')], validators=[validators.InputRequired()])
hidden = HiddenField()
submit = SubmitField('Submit')
And my form is this:
<form autocomplete="off" action="" method="post">
{{ form.hidden_tag() }}
<div style="text-align: center">
<div style="display: inline-block">
{{ form.choice }}
{{ form.hidden(value="{{ result }}") }}
{{ form.submit(class_="btn btn-primary btn-lg") }}
</div>
</div>
</form>
result is a string that I'm passing when rendering the template.
When checking the value of form.hidden.data, though, it comes back as ''. The tag also renders as <input id="hidden" name="hidden" type="hidden" value="">.
I've also tried doing value={{ result }} instead of value="{{result}}" but that makes Jinja throw a TemplateSyntaxError.
Any idea on how to do this?
EDIT:
I'm overwriting result every time I call the function.
This is my route function:
#app.route('/', methods=['GET', 'POST'])
def home():
form = forms.TrueOrFalseForm()
x = random.randint(-100, 100)
y = random.randint(-100, 100)
statement_str = generate_statement_string(2)
tree = BinTree.build_tree(statement_str)
statement_result = BinTree.solve_tree(tree, x, y) # result gets overwritten here
if form.validate_on_submit():
if not flask_login.current_user.is_anonymous:
# same as the else, except with some sql, not relevant
else:
if form.choice.data == form.hidden.data:
flask.flash('Correct!')
else:
flask.flash('Incorrect!')
return flask.render_template('home.html', x_value=str(x), y_value=str(y), statement=statement_str,
result=str(statement_result), form=form)
{{ form.hidden(value="{{ result }}") }} is already in templating syntax with the outer double curly brackets. Therefore, you should just be able to plainly write the result variable, like this: {{ form.hidden(value=result) }}
EDIT
Replace {{ form.hidden_tag() }} with {{ form.csrf_token() }} as well as doing what is in my original answer.
You may also have to instantiate the form with form = forms.TrueOrFalseForm(request.form). Some forms behave weirdly if you don't do that.
Since you're using {{ form.hidden_tag() }} in your template, you do not need to explicitly render the hidden form field. It will be included in the hidden_tag() call.
You can set the value of the hidden field in your views before rendering the template.
views.py
form.hidden.data = result
return render_template("index.html",form=form)
index.html
<form autocomplete="off" action="" method="post">
{{ form.hidden_tag() }}
<div style="text-align: center">
<div style="display: inline-block">
{{ form.choice }}
{{ form.submit(class_="btn btn-primary btn-lg") }}
</div>
</div>
</form>
My proposal is:
<input type="hidden" id="locphoto" value="{{ mbrs.photoName|safe }}" />
Previous answer are correct but I think they need some correction putting safe in variable jinja:

Python Flask endless redirect loop 302

I have currently an issue where one of my pages in flask falls into an endless redirect loop:
I have already so many routes and methodes and had never a problem with this. I also have no idea how to solve it, here is the function / route which has this problem (Its a simple mail function, couldnt even test it):
#app.route('/kontakt/', methods=["GET", "POST"])
def kontakt_send():
form = KontaktMailForm()
if form.validate_on_submit():
if form.check.data == 9:
simplemail.Email(
from_address = form.absender.data, #request.form["absender"],
to_address = u"email#email.de",
subject = u"Nachricht - Kontaktformular von " + form.name.data,
message = form.nachricht.data #request.form["nachricht"]
).send()
flash("Nachricht erfolgreich versandt")
return redirect(url_for('kontakt_send'))
else:
flash("Was ist 4+5?")
return redirect(url_for('kontakt_send'))
else:
flash("Alle Felder muessen ausgefuellt werden")
return redirect(url_for('kontakt_send'))
return render_template('kontakt.html', form=form)
I dont even know which data is relevant to solve this issue, but here is the form from the html template:
<form id="kontaktform" method="POST" enctype="multipart/form-data" action="{{ url_for('kontakt_send') }}">
{{ form.hidden_tag() }}
<div class="form-group">
{{ form.name.label }} <span class="star"> * </span>
{{ form.name(size=30, class = "form-control", placeholder="z.B. Markus Müller") }}
</div>
<div class="form-group">
{{ form.absender.label }} <span class="star"> * </span>
{{ form.absender(class = "form-control", placeholder="z.B. markus.müller#gmx.de") }}
</div>
<div class="form-group">
{{ form.nachricht.label }} <span class="star"> * </span>
{{ form.nachricht(size=500, class = "form-control", placeholder="z.B. Ihre Nachricht an uns") }}
</div>
<div class="form-group">
{{ form.check.label }} <span class="star"> * </span>
{{ form.check(class = "form-control", placeholder="z.B. 9") }}
</div>
<div class="">
<button type="submit" class="btn mybtn"> Senden </button>
</div>
</form>
EDIT:
Remiving the second return redirect(url_for('kontakt_send')) from the else solved the issue, but everytime I load the page it shows me the flash message eventhough I am not submitting the form, how is this possible? It submits the form by reloading the page
validate_on_submit checks two things:
Is the request a POST?
Does the post body validate as the specified form?
If either of these is false, the else block runs and generates the flash message. Since the first check will be false for all GETs, you'll receive the flash message on every page load that isn't a form submission (this is also why you has the redirect loop). The second one will be false anytime the form doesn't validate, thus leading to the flash message again.
All of your if/else blocks lead to flash messages. That's why you get one on every request.

Validate Form Based on Hidden Input's Template Value - Is It Possible?

I have a form that in layman's terms, has a bunch of buttons (that act like checkboxes) with values. Users can select a handful of buttons.
The buttons push their values (via a JQuery function) into a hidden input which I'm using to gather the values.
I would like to make sure that the "values=" attribute of each hidden input isn't null or "" when the user presses the submit form.
Ex: Make sure that the input does NOT equal this:
<input autocomplete="off" id="id_valuePlatform" name="valuePlatform" type="hidden"> or
<input autocomplete="off" id="id_valuePlatform" name="valuePlatform" type="hidden" value="">
Here's the forms.py:
class getGames(forms.Form):
valuePlatform = forms.CharField(required=True, error_messages={'required': 'You need to tell us your platform(s)!'}, widget=forms.HiddenInput(attrs={'autocomplete': 'off'}))
Template:
<form method= "POST" autocomplete="off"> {% csrf_token %}
{{ form.non_field_errors }}
<div class="container">
{% if form.valuePlatform.errors %}
<ol>
{% for error in form.valuePlatform.errors %}
<li><strong>{{ error|escape }}</strong></li>
{% endfor %}
</ol>
{% endif %}
</div>
{{ form.valuePlatform }}
</div>
</div>
</form>
Views.py:
from .forms import getGames
def find(request):
form = getGames()
context = {"form": form}
if form.is_valid():
print form.cleaned_data['valuePlatform']
Is this even possible, or would I have to pass the data to Django via a Ajax POST?
Changed views.py to this, after looking at # Alasdair's examples in the documentation:
from .forms import getGames
def find(request):
form = getGames(request.POST or None)
context = {"form": form}
if request.method == 'POST':
if form.is_valid():
print form.cleaned_data['valuePlatform']

Display form input with Django

So basically I want to make a simple form I can enter text and the after I hit submit, see the text.
Here is my forms.py:
class Search(forms.Form):
search = forms.CharField()
Here is my views.py:
def search(request):
context = RequestContext(request)
if request.method == 'POST':
search = Search(data=request.POST)
if search.is_valid():
ticker = search.save()
ticker.save()
success = True
else:
print search.errors
else:
search = Search()
return render_to_response('ui/search.html', {"search":search}, context)
Here is the html form that you use to type in (I'm using bootstrap for styling purposes):
<form class="navbar-form navbar-right" role="search" action="/search/" method="post" name="tick">
{% csrf_token %}
<div class="form-group">
<input type="text" class="form-control" placeholder="Enter stock symbol">
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
And finally, I want the text entered in the form to be displayed on "search.html" which looks like this currently:
{% extends 'ui/base.html' %}
{% block title %} search {% endblock %}
{% block body_block %}
<br>
<p>test</p>
{{ form.search.data }} <!--I'm pretty sure this is not correct -->
{% endblock %}
Anyone know how I can do this? Thanks.
Your form name is search.
To render the value with modern django, you need to call the value method of the field, therefore your template should look like the following:
{{ search.search.value }}
Your template is wrong, as you suspect.
It is looking for a context variable named "form", but you have given it a context dictionary with a key named "search".
Also, "data" is the argument that you use to build up your Search object (correctly), but when you want to extract the user's input from it, you should use the field names instead, and you need to call value() on them in order to get the bound value. So, to get the contents of the text field called search, you should use search.search.value.
Try changing the line
{{ form.search.data }}
to
{{ search.search.value }}

Categories