Python and Flask: Building a User Registration System

Table of Contents

  1. Introduction
  2. Prerequisites
  3. Project Setup
  4. Creating the User Registration Form
  5. Connecting to a Database
  6. Storing User Information
  7. Logging in Users
  8. Conclusion

Introduction

In this tutorial, we will create a user registration system using Python and Flask. Flask is a web development framework that allows us to build web applications easily. By the end of this tutorial, you will have a working user registration system that can store user information in a database, validate the registration form, and allow users to log in.

Prerequisites

Before starting this tutorial, you should have a basic understanding of Python programming language and HTML. Familiarity with Flask is also beneficial, but not required.

To follow along, make sure you have the following installed:

  • Python 3.x
  • Flask
  • SQLite or any other compatible database system

Project Setup

Let’s begin by setting up the project. Create a new directory for your project and navigate to it in your terminal. Once inside the project directory, create a virtual environment by running the following command: python3 -m venv venv Activate the virtual environment by running: source venv/bin/activate Now, let’s install Flask using pip: pip install flask Great! We are now ready to start building our user registration system.

Creating the User Registration Form

HTML Template

First, let’s create a basic HTML template for the user registration form. Create a new file called register.html and add the following code: ```html <!DOCTYPE html> <html> <head> User Registration </head> <body> <h1>User Registration</h1> <form method="POST" action="/register">

        <label for="email">Email:</label>
        <input type="email" id="email" name="email" required><br><br>
        
        <label for="password">Password:</label>
        <input type="password" id="password" name="password" required><br><br>
        
        <input type="submit" value="Register">
    </form>
</body>
</html>
``` This HTML template defines a simple user registration form with fields for username, email, and password.

Flask Routes

Next, let’s create the Flask routes to handle the user registration form. In your project directory, create a new Python file named app.py and add the following code: ```python from flask import Flask, render_template, request

app = Flask(__name__)

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

@app.route('/register', methods=['POST'])
def register():
    username = request.form['username']
    email = request.form['email']
    password = request.form['password']
    
    # TODO: Store user information in the database
    
    return 'Registration successful!'

if __name__ == '__main__':
    app.run(debug=True)
``` In the above code, we have defined two Flask routes. The `/` route renders the `register.html` template, which displays the user registration form. The `/register` route handles the form submission and retrieves the form data using `request.form`. Note that the TODO comment indicates that we will add code to store the user information in the database later.

To run the Flask application, execute the following command in your terminal: python app.py Visit http://localhost:5000 in your web browser, and you should see the user registration form.

Form Validation

Now, let’s add form validation to our user registration system. We will use the Flask-WTF extension to simplify form handling and validation.

First, let’s install Flask-WTF: pip install flask-wtf Next, update your app.py file with the following code: ```python from flask import Flask, render_template, request, flash, redirect, url_for from flask_wtf import FlaskForm from wtforms import StringField, PasswordField from wtforms.validators import InputRequired, Email, Length

app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret-key'

class RegistrationForm(FlaskForm):
    username = StringField('Username', validators=[InputRequired(), Length(min=4, max=20)])
    email = StringField('Email', validators=[InputRequired(), Email()])
    password = PasswordField('Password', validators=[InputRequired(), Length(min=8)])

@app.route('/', methods=['GET', 'POST'])
def index():
    form = RegistrationForm()
    if form.validate_on_submit():
        flash('Registration successful!', 'success')
        return redirect(url_for('index'))
    return render_template('register.html', form=form)

if __name__ == '__main__':
    app.run(debug=True)
``` In the updated code, we have defined a `RegistrationForm` class using Flask-WTF and WTForms. This class represents the user registration form and defines the fields and validation rules. We have also added a secret key to the Flask application's config, which is required by Flask-WTF.

Inside the / route, we create an instance of RegistrationForm and pass it to the register.html template. We check if the form is submitted and valid using form.validate_on_submit(). If the validation is successful, we display a flash message and redirect the user back to the registration form.

To see the form validation in action, run the Flask application and fill in the registration form with invalid data. You should see error messages displayed next to the respective form fields.

Connecting to a Database

Now that we have a working user registration form with validation, let’s connect our application to a database to store user information.

SQLite Database Setup

For simplicity, we will use SQLite as our database system. SQLite is a lightweight database engine that stores the database in a single file.

To set up SQLite, run the following command in your terminal: pip install flask_sqlalchemy Next, update your app.py file with the following code: ```python from flask import Flask, render_template, request, flash, redirect, url_for from flask_wtf import FlaskForm from wtforms import StringField, PasswordField from wtforms.validators import InputRequired, Email, Length from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret-key'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///database.db'

db = SQLAlchemy(app)

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(80), nullable=False)

    def __repr__(self):
        return f'<User {self.username}>'

class RegistrationForm(FlaskForm):
    username = StringField('Username', validators=[InputRequired(), Length(min=4, max=20)])
    email = StringField('Email', validators=[InputRequired(), Email()])
    password = PasswordField('Password', validators=[InputRequired(), Length(min=8)])

@app.route('/', methods=['GET', 'POST'])
def index():
    form = RegistrationForm()
    if form.validate_on_submit():
        new_user = User(username=form.username.data, email=form.email.data, password=form.password.data)
        db.session.add(new_user)
        db.session.commit()
        flash('Registration successful!', 'success')
        return redirect(url_for('index'))
    return render_template('register.html', form=form)

if __name__ == '__main__':
    app.run(debug=True)
``` In the updated code, we have added the necessary configuration to connect the Flask application to a SQLite database. The `SQLALCHEMY_DATABASE_URI` points to a file named `database.db` in the same directory as the `app.py` file. We have also defined a `User` model that represents a user in the database. The model has fields for the username, email, and password.

Inside the / route, after validating the form, we create a new instance of User and add it to the database session using db.session.add(). Finally, we commit the changes to the database using db.session.commit().

To initialize the database, execute the following commands in your terminal: python python from app import db db.create_all() exit() This will create the necessary tables in the SQLite database file.

Storing User Information

Now that our application is connected to a database, let’s update the user registration process to handle duplicate user registration attempts and display appropriate error messages.

Update your app.py file with the following code: ```python from flask import Flask, render_template, request, flash, redirect, url_for from flask_wtf import FlaskForm from wtforms import StringField, PasswordField from wtforms.validators import InputRequired, Email, Length, ValidationError from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret-key'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///database.db'

db = SQLAlchemy(app)

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(80), nullable=False)

    def __repr__(self):
        return f'<User {self.username}>'

class RegistrationForm(FlaskForm):
    username = StringField('Username', validators=[InputRequired(), Length(min=4, max=20)])
    email = StringField('Email', validators=[InputRequired(), Email()])
    password = PasswordField('Password', validators=[InputRequired(), Length(min=8)])

    def validate_username(self, field):
        if User.query.filter_by(username=field.data).first():
            raise ValidationError('Username is already taken.')

    def validate_email(self, field):
        if User.query.filter_by(email=field.data).first():
            raise ValidationError('Email is already registered.')

@app.route('/', methods=['GET', 'POST'])
def index():
    form = RegistrationForm()
    if form.validate_on_submit():
        new_user = User(username=form.username.data, email=form.email.data, password=form.password.data)
        db.session.add(new_user)
        db.session.commit()
        flash('Registration successful!', 'success')
        return redirect(url_for('index'))
    return render_template('register.html', form=form)

if __name__ == '__main__':
    app.run(debug=True)
``` In the updated code, we have added custom validation methods to the `RegistrationForm` class. These methods check if the entered username or email already exists in the database using `User.query.filter_by()`. If a duplicate entry is found, a `ValidationError` is raised with the appropriate error message.

Now, if a user tries to register with a username or email that already exists, an error message will be displayed on the registration form.

Logging in Users

Our user registration system is almost complete. Let’s add the ability for users to log in.

Update your app.py file with the following code: ```python from flask import Flask, render_template, request, flash, redirect, url_for from flask_login import LoginManager, UserMixin, login_user, logout_user, login_required from flask_wtf import FlaskForm from wtforms import StringField, PasswordField from wtforms.validators import InputRequired, Email, Length, ValidationError from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret-key'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///database.db'

db = SQLAlchemy(app)

class User(UserMixin, 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(80), nullable=False)

    def __repr__(self):
        return f'<User {self.username}>'

class RegistrationForm(FlaskForm):
    username = StringField('Username', validators=[InputRequired(), Length(min=4, max=20)])
    email = StringField('Email', validators=[InputRequired(), Email()])
    password = PasswordField('Password', validators=[InputRequired(), Length(min=8)])

    def validate_username(self, field):
        if User.query.filter_by(username=field.data).first():
            raise ValidationError('Username is already taken.')

    def validate_email(self, field):
        if User.query.filter_by(email=field.data).first():
            raise ValidationError('Email is already registered.')

class LoginForm(FlaskForm):
    username = StringField('Username', validators=[InputRequired()])
    password = PasswordField('Password', validators=[InputRequired()])

login_manager = LoginManager()
login_manager.init_app(app)

@login_manager.user_loader
def load_user(user_id):
    return User.query.get(int(user_id))

@app.route('/', methods=['GET', 'POST'])
def index():
    form = RegistrationForm()
    if form.validate_on_submit():
        new_user = User(username=form.username.data, email=form.email.data, password=form.password.data)
        db.session.add(new_user)
        db.session.commit()
        flash('Registration successful!', 'success')
        return redirect(url_for('login'))
    return render_template('register.html', form=form)

@app.route('/login', methods=['GET', 'POST'])
def login():
    form = LoginForm()
    if form.validate_on_submit():
        user = User.query.filter_by(username=form.username.data).first()
        if user and user.password == form.password.data:
            login_user(user)
            return redirect(url_for('dashboard'))
        flash('Invalid username or password.', 'danger')
    return render_template('login.html', form=form)

@app.route('/dashboard')
@login_required
def dashboard():
    return 'Welcome to the dashboard!'

@app.route('/logout')
@login_required
def logout():
    logout_user()
    return redirect(url_for('login'))

if __name__ == '__main__':
    app.run(debug=True)
``` In the updated code, we have added a login form and implemented user authentication using `flask_login`. We have also defined a `LoginForm` class representing the login form with fields for the username and password.

Inside the /login route, we check if the submitted username and password match a user in the database. If the login is successful, we call login_user() to log in the user and redirect them to the dashboard. If the login is unsuccessful, an error message is displayed.

To log out the user, we have added a /logout route that calls logout_user() and redirects the user to the login page.

Now, your user registration system is complete! Users can register, log in, and log out of your application.

Conclusion

In this tutorial, we have built a user registration system using Python and Flask. We covered the basics of Flask, form validation, connecting to a database, storing user information, and implementing user authentication.

With this knowledge, you can expand upon this project to add additional features like password hashing, account activation emails, user roles, and profile pages.

Feel free to explore Flask’s extensive documentation to learn more about its capabilities and get creative with your own web development projects. Keep practicing, and happy coding!