56: Flask Forms - MantsSk/CA_PTUA14 GitHub Wiki

Introduction to Forms in Flask

In web development, forms are essential for collecting and processing user input. Flask, offers two main ways to work with forms: using plain HTML forms and using Flask-WTF (Flask Form). Each approach has its own benefits and use cases.

Using HTML Forms in Flask

HTML forms are the backbone of web-based data collection. In Flask, you can create HTML forms directly within your templates using the form tag and various input elements such as input, textarea, and select. Here's a basic example of an HTML form in a Flask template:

In this form:

  • method="POST" specifies that the form data should be sent to the server using the HTTP POST method.
  • action="/submit" specifies the URL where the form data should be submitted.
  • Each input field has a name attribute, which is used to identify the form fields when the form is submitted.
  • The required attribute is used to specify that a field must be filled out before submitting the form.

To handle form submissions in Flask, you can use the request object to access form data in your view functions. Here's an example of how you might handle the form submission:

from flask import Flask, request

app = Flask(__name__)

@app.route('/submit', methods=['POST'])
def submit_form():
    username = request.form['username']
    password = request.form['password']
    # Process the form data (e.g., validate, save to database, etc.)
    return 'Form submitted successfully!'

if __name__ == '__main__':
    app.run(debug=True)

Notice that we added additional argument in app.route: methods="POST. This will let us our route not only perform GET requests, but also POST request as well.

You can upgrade this function to send data to database:

@app.route('/submit', methods=['POST'])
def submit_form():
    username = request.form['username']
    password = request.form['password']
    new_user = User(username=username, password=password)
    db.session.add(new_user)
    db.session.commit()
    return 'Form submitted successfully!'

Full code would look like this:

app.py:

from flask import Flask, request, render_template
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///users.db'
db = SQLAlchemy(app)

class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True, nullable=False)
    password = db.Column(db.String(120), nullable=False)

@app.route('/')
def home():
    return render_template('index.html')

@app.route('/submit', methods=['POST'])
def submit_form():
    username = request.form['username']
    password = request.form['password']
    new_user = User(username=username, password=password)
    db.session.add(new_user)
    db.session.commit()
    return 'Form submitted successfully!'

if __name__ == '__main__':
    with app.app_context():
        db.create_all()
        
    app.run(debug=True)

index.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>HTML Form</title>
</head>
<body>
    <form method="POST" action="/submit">
        <label for="username">Username:</label>
        <input type="text" id="username" name="username" required>
        <label for="password">Password:</label>
        <input type="password" id="password" name="password" required>
        <button type="submit">Submit</button>
    </form>
</body>
</html>

Using Flask-WTF instead

Install flask-wtf with:

pip install Flask-WTF
pip install WTForms

Flask-WTF is a Flask extension that makes working with forms even easier by providing a higher-level interface for defining and validating forms. It integrates seamlessly with Flask and allows you to define forms as classes. Here's an example of how to define a form using Flask-WTF:

from flask import Flask, render_template
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, SubmitField
from wtforms.validators import DataRequired

app = Flask(__name__)
app.config['SECRET_KEY'] = 'your_secret_key'

class LoginForm(FlaskForm):
    username = StringField('Username', validators=[DataRequired()])
    password = PasswordField('Password', validators=[DataRequired()])
    submit = SubmitField('Submit')

@app.route('/login', methods=['GET', 'POST'])
def login():
    form = LoginForm()
    if form.validate_on_submit():
        # Process the form data (e.g., validate, save to database, etc.)
        return 'Login successful!'
    return render_template('login.html', form=form)

if __name__ == '__main__':
    app.run(debug=True)

In this example:

  • We define a LoginForm class that inherits from FlaskForm.
  • We use StringField, PasswordField, and SubmitField to define the form fields.
  • We specify validators such as DataRequired to ensure that the fields are not submitted empty.
  • In the view function, we create an instance of LoginForm and pass it to the template. When the form is submitted, validate_on_submit() validates the form data and returns True if all validators pass.

In the template part we would get info from LoginForm we passed:

<form method="POST" action="/submit">
    {{ form.csrf_token }}
    {{ form.username.label }} {{ form.username }}
    {{ form.password.label }} {{ form.password }}
    {{ form.submit }}
</form>

  • {{ form.csrf_token }}: This renders a hidden input field containing a CSRF token. Flask-WTF automatically adds this field to protect against Cross-Site Request Forgery (CSRF) attacks.
  • {{ form.username.label }} {{ form.username }}: This renders the label and input field for the "username" field of the Flask-WTF form object. {{ form.username.label }} renders the label text for the username field, and {{ form.username }} renders the input field itself.
  • {{ form.password.label }} {{ form.password }}: Similar to the username field, this renders the label and input field for the "password" field of the Flask-WTF form object.
  • {{ form.submit }}: This renders the submit button for the form. form.submit refers to the submit button field defined in the Flask-WTF form object.

Full code would look something like this.

app.py:

from flask import Flask, render_template, redirect, url_for
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, SubmitField
from wtforms.validators import DataRequired
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
app.config['SECRET_KEY'] = 'your_secret_key'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///users.db'
db = SQLAlchemy(app)

class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True, nullable=False)
    password = db.Column(db.String(120), nullable=False)

class LoginForm(FlaskForm):
    username = StringField('Username', validators=[DataRequired()])
    password = PasswordField('Password', validators=[DataRequired()])
    submit = SubmitField('Submit')

@app.route('/')
def login():
    form = LoginForm()
    return render_template('index.html', form=form)

@app.route('/submit', methods=['POST'])
def submit_form():
    form = LoginForm()
    if form.validate_on_submit():
        username = form.username.data
        password = form.password.data
        new_user = User(username=username, password=password)
        db.session.add(new_user)
        db.session.commit()
        return 'User added to database!'
    return render_template('login.html', form=form)

if __name__ == '__main__':
    with app.app_context():
        db.create_all()
        
    app.run(debug=True)

index.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Login Form</title>
</head>
<body>
    <form method="POST" action="/submit">
        {{ form.csrf_token }}
        {{ form.username.label }} {{ form.username }}
        {{ form.password.label }} {{ form.password }}
        {{ form.submit }}
    </form>
</body>
</html>

Exercise

Create a web application with login and register functionality for users to register and log in. The register page should feature an HTML form with email and password fields, while the login page should utilize a Flask WTF form with email and password fields. Upon successful registration, display "Successful registration". Upon successful login, display "Logged in successfully". On login failure, display "Incorrect email or password".

  1. Define User Database Model:
  • Create a User database model with the following attributes:
    • id: Primary key
    • email: Unique and not nullable string
    • password: Not nullable string
  1. Create Register Page:
  • Define a route "/register" for the register page.
  • Implement a function to handle GET and POST requests.
  • On GET request, render the register template (register form).
  • On POST request, handle registration form data.
  • If registration is successful, return "Successful registration!" text.
  1. Create Register HTML Template:
  • Create a "register.html" template.
  • Design the template to include a register form with:
    • Email field
    • Password field
    • Submit button
  1. Create Login Page:
  • Define a route "/login" for the login page.
  • Implement a function to handle GET and POST requests.
  • On GET request, render the login template (login form).
  • On POST request, handle login form data.
  • If login is successful, return "Logged in successfully" text.
  • If login fails, return "Invalid email or password" text. (Check if user with provided email and password exists)
  1. Create Login HTML Template:
  • Create a "login.html" template.
  • Design the template to include a login form with:
    • Email field
    • Password field
    • Submit button
  1. Utilize Flask-WTF for Login Form:
  • Create a Flask WTF form for the login page.
  • Include email and password fields with required validations.
  1. Implement Login Form Validations:
  • Ensure that the email field has email and required validations.
  • Ensure that the password field has a required validation.
⚠️ **GitHub.com Fallback** ⚠️