Chapter 7 of 10
Flask doesn't include form handling — that's where Flask-WTF (WTForms integration) comes in. It adds CSRF protection, validation, and HTML rendering.
pip install flask-wtffrom flask_wtf import FlaskForm
from wtforms import StringField, TextAreaField, SelectField, SubmitField
from wtforms.validators import DataRequired, Email, Length
app.config["SECRET_KEY"] = "your-secret-key" # required for CSRF
class ContactForm(FlaskForm):
name = StringField("Name", validators=[DataRequired(), Length(min=2, max=100)])
email = StringField("Email", validators=[DataRequired(), Email()])
course = SelectField("Course", choices=[("html", "HTML"), ("css", "CSS")])
message = TextAreaField("Message", validators=[DataRequired(), Length(min=10)])
submit = SubmitField("Send")
@app.route("/contact", methods=["GET", "POST"])
def contact():
form = ContactForm()
if form.validate_on_submit(): # validates on POST only
name = form.name.data
email = form.email.data
message = form.message.data
send_email(name, email, message)
flash("Message sent!", "success")
return redirect(url_for("contact"))
return render_template("contact.html", form=form)<form method="POST">
{{ form.hidden_tag() }} {# CSRF token — required! #}
<div>
{{ form.name.label }}
{{ form.name(placeholder="Your name") }}
{% for error in form.name.errors %}
<span class="error">{{ error }}</span>
{% endfor %}
</div>
<div>
{{ form.email.label }}
{{ form.email() }}
{% for error in form.email.errors %}
<span class="error">{{ error }}</span>
{% endfor %}
</div>
<div>{{ form.submit() }}</div>
</form>