In this Flask Tutorial we are going to learn how to create Flask Forms with Flask-WTF, Flask-WTF is simple integration of Flask and WTForms, including CSRF, file upload, and reCAPTCHA.
Features :
- Integration with WTForms.
- Secure Form with CSRF token.
- Global CSRF protection.
- reCAPTCHA support.
- File upload that works with Flask-Uploads.
- Internationalization using Flask-Babel.
Installation
First of all you need to install Flask-WTF, you can easily install Flask-WTF using pip command.
1 |
pip install Flask-WTF |
First of all you need to create a new python file at name of form.py, basically in this file we are going to create our form. also you can see that we have used validators for our input fields. a validator simply takes an input, verifies it fulfills some criterion, such as a maximum length for a string and returns. Or, if the validation fails, raises a ValidationError this system is very simple and flexible, and allows you to chain any number of validators on fields.
1 2 3 4 5 6 7 8 9 10 |
from flask_wtf import FlaskForm from wtforms import StringField, PasswordField, SubmitField from wtforms.validators import InputRequired class LoginForm(FlaskForm): username = StringField('Username', validators=[InputRequired()]) password = PasswordField('Password', validators=[InputRequired()]) submit = SubmitField('Login') |
Now let’s add a button in our NavBar for login, so open your base.html and add this code before closing the navbar.
1 2 3 |
<a href="{{url_for('Login')}}"> <button class="btn btn-success navbar-btn">Login</button> </a> |
First we need to import our LoginForm from form.py file.
1 |
from form import LoginForm |
When you are working with forms, than you need to add secret key, it will protect you from CSRF attacks.
1 |
app.config['SECRET_KEY'] = 'hardsecretkey' |
Now let’s open our app.py file, because we want to add a login route, in this route first we create the object of our LoginForm and after that we send that to the login.html file that we will create, also we have added our methods in the Login route.
1 2 3 4 5 6 |
@app.route('/login', methods = ['GET', 'POST']) def Login(): form = LoginForm() if form.validate_on_submit(): return 'Form Submited .. ' return render_template('login.html', form=form) |
Let’s create our login.html file in our templates folder, in here we are rendering the fields from the login that we are receiving via context variable.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
{% extends 'base.html' %} {% block title %}Login{% endblock %} {% block body %} <div class="container"> <h1>Login here</h1> <form action="" method="post" novalidate> {{form.csrf_token}} <p> {{form.username.label}} {{form.username(size=32)}} </p> <p> {{form.password.label}} {{form.password(size=32)}} </p> <p> {{form.submit()}} </p> </form> </div> {% endblock %} |
Rest of the files.
app.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
from flask import Flask, render_template from form import LoginForm #create the object of Flask app = Flask(__name__) app.config['SECRET_KEY'] = 'hardsecretkey' #creating our routes @app.route('/') def Index(): name = 'Geekscoders.com' return render_template('index.html', data = name) @app.route('/login', methods = ['GET', 'POST']) def Login(): form = LoginForm() if form.validate_on_submit(): return 'Form Submited .. ' return render_template('login.html', form=form) @app.route('/contact') def Contact(): return render_template('contact.html') @app.route('/about') def About(): return render_template('about.html') @app.errorhandler(404) def page_not_found(e): return render_template('404.html') @app.errorhandler(500) def internal_server_error(e): return render_template('500.html') #run flask app if __name__ == "__main__": app.run(debug=True) |
templates/base.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>{% block title %}{% endblock %}</title> <!-- CSS only --> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.1/dist/css/bootstrap.min.css" rel="stylesheet" > <!-- Add Icon --> <link rel="icon" href="{{url_for('static', filename = 'py.png')}}"/> </head> <body> <nav class="navbar navbar-expand-lg navbar navbar-dark bg-dark"> <div class="container-fluid"> <a class="navbar-brand" href="{{url_for('Index')}}">GeeksCoders</a> <div class="collapse navbar-collapse" id="navbarSupportedContent"> <ul class="navbar-nav me-auto mb-2 mb-lg-0"> <li class="nav-item"> <a class="nav-link active" aria-current="page" href="{{url_for('Index')}}">Home</a> </li> <li class="nav-item"> <a class="nav-link" href="{{url_for('Contact')}}">Contact</a> </li> <li class="nav-item"> <a class="nav-link" href="{{url_for('About')}}">About</a> </li> </ul> <form class="d-flex"> <input class="form-control me-2" type="search" placeholder="Search" aria-label="Search"> <button class="btn btn-outline-success" type="submit">Search</button> </form> </div> </div> <a href="{{url_for('Login')}}"> <button class="btn btn-success navbar-btn">Login</button> </a> </nav> {% block body %} {% endblock %} <!-- JavaScript Bundle with Popper --> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.1/dist/js/bootstrap.bundle.min.js" > </script> </body> </html> |
templates/index.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
{% extends 'base.html' %} {% block title %}Home{% endblock %} {% block body %} <div class="container"> <h1>Welcome to our home page</h1> <h3>Welcome to . {{data}}</h3> </div> {% endblock %} |
templates/contact.html
1 2 3 4 5 6 7 8 9 10 11 12 13 |
{% extends 'base.html' %} {% block title %}Contact{% endblock %} {% block body %} <div class="container"> <h1>This is contact page</h1> </div> {% endblock %} |
templates/about.html
1 2 3 4 5 6 7 8 9 10 11 12 13 |
{% extends 'base.html' %} {% block title %}About{% endblock %} {% block body %} <div class="container"> <h1>This is our about page</h1> </div> {% endblock %} |
templates/500.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
{% extends 'base.html' %} {% block title %}Server Error {% endblock %} {% block body %} <div class="container"> <h1>Internal Server Error </h1> </div> {% endblock %} |
templates/404.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
{% extends 'base.html' %} {% block title %}Page Not Found {% endblock %} {% block body %} <div class="container"> <h1>Page Not Found</h1> </div> {% endblock %} |
Run your project click on the login button, this will be the result.