python 48 lines · 10 steps

A multi-step signup wizard in Flask

A Flask Blueprint spreads a signup form across two steps, stashing each step's data in the session until the account is created.

Explained by highlit
1from flask import Blueprint, render_template, redirect, url_for, session, request
2from wtforms import Form, StringField, SelectField, IntegerField
3from wtforms.validators import DataRequired, Email, NumberRange
4 
5signup = Blueprint("signup", __name__, url_prefix="/signup")
6 
7 
8class AccountForm(Form):
9 full_name = StringField("Full name", validators=[DataRequired()])
10 email = StringField("Email", validators=[DataRequired(), Email()])
11 
12 
13class PlanForm(Form):
14 plan = SelectField("Plan", choices=[("free", "Free"), ("pro", "Pro")])
15 seats = IntegerField("Seats", validators=[NumberRange(min=1, max=50)])
16 
17 
18def _wizard():
19 return session.setdefault("signup_wizard", {})
20 
21 
22@signup.route("/step-1", methods=["GET", "POST"])
23def step_one():
24 form = AccountForm(request.form, data=_wizard())
25 if request.method == "POST" and form.validate():
26 _wizard().update(form.data)
27 session.modified = True
28 return redirect(url_for("signup.step_two"))
29 return render_template("signup/step_1.html", form=form)
30 
31 
32@signup.route("/step-2", methods=["GET", "POST"])
33def step_two():
34 if not _wizard().get("email"):
35 return redirect(url_for("signup.step_one"))
36 
37 form = PlanForm(request.form, data=_wizard())
38 if request.method == "POST" and form.validate():
39 _wizard().update(form.data)
40 user = User.create_from_wizard(session.pop("signup_wizard"))
41 session.modified = True
42 return redirect(url_for("dashboard.home", welcome=user.id))
43 return render_template("signup/step_2.html", form=form)
44 
45 
46@signup.route("/back")
47def back():
48 return redirect(url_for("signup.step_one"))
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Storing intermediate form data in the session lets a single workflow span multiple requests and pages.
  2. 2Guarding later steps against missing earlier data keeps users from skipping ahead via direct URLs.
  3. 3A Blueprint groups related routes under one URL prefix so a feature stays self-contained.

Related explainers

Share this explainer

Here's the card — post it anywhere.

A multi-step signup wizard in Flask — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code