Skip to main content

Flask User Registration

User registration is a fundamental feature in many web applications, allowing visitors to create accounts, save preferences, and access protected content. In this tutorial, we'll learn how to build a robust user registration system using Flask.

Introduction to User Registration

A user registration system typically consists of:

  • A registration form that collects user information
  • Validation logic to ensure data integrity
  • Password security measures
  • Database storage for user credentials
  • Feedback mechanisms to guide users

Flask makes implementing these features straightforward with its extensions and flexible architecture.

Prerequisites

Before starting, make sure you have:

  • Basic knowledge of Flask
  • Python 3.6 or higher installed
  • Understanding of HTML forms
  • Basic SQL knowledge

Setting Up Your Flask Project

Let's start by setting up a basic Flask application structure:

bash
mkdir flask_user_registration
cd flask_user_registration
python -m venv venv
# On Windows
venv\Scripts\activate
# On macOS/Linux
source venv/bin/activate
pip install flask flask-sqlalchemy flask-wtf email-validator flask-bcrypt

Now create a basic project structure:

flask_user_registration/
├── app.py
├── models.py
├── forms.py
├── templates/
│ ├── layout.html
│ ├── home.html
│ └── register.html
└── static/
└── styles.css

Creating the Database Model

First, let's define our user model in models.py:

python
from flask_sqlalchemy import SQLAlchemy
from datetime import datetime

db = SQLAlchemy()

class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(20), unique=True, nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)
password = db.Column(db.String(60), nullable=False)
date_joined = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)

def __repr__(self):
return f"User('{self.username}', '{self.email}')"

This model defines a User table with fields for username, email, password (which we'll hash for security), and registration date.

Building the Registration Form

Next, let's create our registration form using Flask-WTF in forms.py:

python
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, SubmitField
from wtforms.validators import DataRequired, Length, Email, EqualTo, ValidationError
from models import User

class RegistrationForm(FlaskForm):
username = StringField('Username',
validators=[DataRequired(), Length(min=2, max=20)])
email = StringField('Email',
validators=[DataRequired(), Email()])
password = PasswordField('Password',
validators=[DataRequired()])
confirm_password = PasswordField('Confirm Password',
validators=[DataRequired(), EqualTo('password')])
submit = SubmitField('Sign Up')

# Custom validation to check if username already exists
def validate_username(self, username):
user = User.query.filter_by(username=username.data).first()
if user:
raise ValidationError('That username is already taken. Please choose another one.')

# Custom validation to check if email already exists
def validate_email(self, email):
user = User.query.filter_by(email=email.data).first()
if user:
raise ValidationError('That email is already registered. Please use another one.')

This form includes fields for username, email, password with confirmation, and custom validation methods to ensure usernames and emails are unique.

Creating HTML Templates

Now let's create our templates. First, the base layout in templates/layout.html:

html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Flask User Registration</title>
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='styles.css') }}">
</head>
<body>
<header>
<nav>
<div class="container">
<h1>Flask Auth Demo</h1>
<div class="nav-links">
<a href="{{ url_for('home') }}">Home</a>
<a href="{{ url_for('register') }}">Register</a>
</div>
</div>
</nav>
</header>

<main class="container">
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
{% for category, message in messages %}
<div class="alert alert-{{ category }}">
{{ message }}
</div>
{% endfor %}
{% endif %}
{% endwith %}

{% block content %}{% endblock %}
</main>
</body>
</html>

Next, create a simple home page in templates/home.html:

html
{% extends "layout.html" %}

{% block content %}
<section class="welcome-section">
<h2>Welcome to Flask Authentication Demo</h2>
<p>This simple application demonstrates user registration in Flask.</p>
<div class="cta-buttons">
<a href="{{ url_for('register') }}" class="button">Register Now</a>
</div>
</section>
{% endblock %}

Finally, the registration form in templates/register.html:

html
{% extends "layout.html" %}

{% block content %}
<div class="form-container">
<h2>Create an Account</h2>
<form method="POST" action="">
{{ form.hidden_tag() }}
<div class="form-group">
{{ form.username.label(class="form-label") }}
{% if form.username.errors %}
{{ form.username(class="form-input is-invalid") }}
<div class="invalid-feedback">
{% for error in form.username.errors %}
<span>{{ error }}</span>
{% endfor %}
</div>
{% else %}
{{ form.username(class="form-input") }}
{% endif %}
</div>

<div class="form-group">
{{ form.email.label(class="form-label") }}
{% if form.email.errors %}
{{ form.email(class="form-input is-invalid") }}
<div class="invalid-feedback">
{% for error in form.email.errors %}
<span>{{ error }}</span>
{% endfor %}
</div>
{% else %}
{{ form.email(class="form-input") }}
{% endif %}
</div>

<div class="form-group">
{{ form.password.label(class="form-label") }}
{% if form.password.errors %}
{{ form.password(class="form-input is-invalid") }}
<div class="invalid-feedback">
{% for error in form.password.errors %}
<span>{{ error }}</span>
{% endfor %}
</div>
{% else %}
{{ form.password(class="form-input") }}
{% endif %}
</div>

<div class="form-group">
{{ form.confirm_password.label(class="form-label") }}
{% if form.confirm_password.errors %}
{{ form.confirm_password(class="form-input is-invalid") }}
<div class="invalid-feedback">
{% for error in form.confirm_password.errors %}
<span>{{ error }}</span>
{% endfor %}
</div>
{% else %}
{{ form.confirm_password(class="form-input") }}
{% endif %}
</div>

<div class="form-group">
{{ form.submit(class="button") }}
</div>
</form>
<div class="form-footer">
<p>Already have an account? <a href="#">Login here</a></p>
</div>
</div>
{% endblock %}

Adding Some Basic Styling

Create a minimal stylesheet in static/styles.css:

css
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}

body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
line-height: 1.6;
background-color: #f7f9fc;
color: #333;
}

.container {
width: 90%;
max-width: 1200px;
margin: 0 auto;
padding: 0 15px;
}

header {
background-color: #4a6fa5;
color: white;
padding: 1rem 0;
margin-bottom: 2rem;
}

nav .container {
display: flex;
justify-content: space-between;
align-items: center;
}

.nav-links a {
color: white;
margin-left: 1rem;
text-decoration: none;
}

.form-container {
background-color: white;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
padding: 2rem;
max-width: 600px;
margin: 0 auto;
}

.form-container h2 {
margin-bottom: 1.5rem;
text-align: center;
color: #4a6fa5;
}

.form-group {
margin-bottom: 1rem;
}

.form-label {
display: block;
margin-bottom: 0.5rem;
font-weight: 500;
}

.form-input {
width: 100%;
padding: 0.75rem;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 1rem;
}

.form-input.is-invalid {
border-color: #dc3545;
}

.invalid-feedback {
color: #dc3545;
font-size: 0.875rem;
margin-top: 0.25rem;
}

.button {
display: inline-block;
background-color: #4a6fa5;
color: white;
border: none;
padding: 0.75rem 1.5rem;
font-size: 1rem;
border-radius: 4px;
cursor: pointer;
text-decoration: none;
}

.button:hover {
background-color: #3a5a80;
}

.form-footer {
text-align: center;
margin-top: 1.5rem;
}

.alert {
padding: 1rem;
margin-bottom: 1rem;
border-radius: 4px;
}

.alert-success {
background-color: #d4edda;
color: #155724;
}

.alert-danger {
background-color: #f8d7da;
color: #721c24;
}

.welcome-section {
text-align: center;
padding: 2rem 0;
}

.welcome-section h2 {
margin-bottom: 1rem;
color: #4a6fa5;
}

.cta-buttons {
margin-top: 2rem;
}

Implementing the Flask Application

Finally, let's implement the main Flask application in app.py:

python
from flask import Flask, render_template, url_for, flash, redirect
from flask_bcrypt import Bcrypt
from forms import RegistrationForm
from models import db, User

app = Flask(__name__)
app.config['SECRET_KEY'] = 'your_secret_key_here' # Change this in production!
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///site.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

# Initialize extensions
bcrypt = Bcrypt(app)
db.init_app(app)

# Create database tables
@app.before_first_request
def create_tables():
db.create_all()

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

@app.route('/register', methods=['GET', 'POST'])
def register():
form = RegistrationForm()
if form.validate_on_submit():
# Hash the password
hashed_password = bcrypt.generate_password_hash(form.password.data).decode('utf-8')

# Create new user
user = User(username=form.username.data,
email=form.email.data,
password=hashed_password)

# Add to database
db.session.add(user)
db.session.commit()

flash(f'Account created for {form.username.data}! You can now log in.', 'success')
return redirect(url_for('home'))

return render_template('register.html', form=form)

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

Running the Application

Now you can run your Flask application:

bash
python app.py

Visit http://127.0.0.1:5000/ in your browser to see the home page, and navigate to the registration page to test your new user registration system.

Security Considerations

Our implementation includes several security best practices:

  1. Password Hashing: We use Flask-Bcrypt to securely hash passwords before storing them
  2. Form Validation: Flask-WTF provides CSRF protection and data validation
  3. Feedback: Users receive clear feedback on registration success or errors
  4. Field Uniqueness: The system prevents duplicate usernames and emails

Adding Additional Features

Here are some ways to enhance your registration system:

Email Confirmation

To add email verification:

  1. Generate a token when a user registers
  2. Send an email with a confirmation link
  3. Verify the token when the user clicks the link
  4. Only activate the account after verification

Password Strength Requirements

Enhance the password field validation:

python
from wtforms.validators import Regexp

password = PasswordField('Password',
validators=[
DataRequired(),
Length(min=8),
Regexp('^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$',
message='Password must contain at least one uppercase letter, one lowercase letter, one number, and one special character')
])

CAPTCHA Integration

To prevent automated registrations, you could add a CAPTCHA using flask-recaptcha:

python
from flask_recaptcha import ReCaptcha

# In your app setup
recaptcha = ReCaptcha(app=app)
app.config['RECAPTCHA_SITE_KEY'] = 'your-site-key'
app.config['RECAPTCHA_SECRET_KEY'] = 'your-secret-key'

# In your form
from flask_recaptcha import ReCaptcha
recaptcha = ReCaptcha()

# In your template
{{ recaptcha }}

# In your route
if form.validate_on_submit() and recaptcha.verify():
# Process registration

Summary

In this tutorial, we've built a complete user registration system in Flask that:

  1. Creates a registration form with validation
  2. Securely hashes passwords
  3. Stores user information in a database
  4. Provides feedback to users
  5. Prevents duplicate registrations

This registration system provides a solid foundation that you can extend with additional features like email verification, social login, or password strength requirements.

Additional Resources

Exercises

  1. Add a login page that authenticates users against the registered credentials
  2. Implement password reset functionality
  3. Add email verification using tokens
  4. Create a user profile page that shows registration date
  5. Enhance the registration form with additional fields like first name, last name, etc.

By completing this tutorial, you now have a solid understanding of how to implement user registration in Flask applications. This knowledge forms the foundation for building secure, user-centric web applications.



If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)