Simple Registration and Login Form using Flask and Python

Author: Robin Design Services | Date Created: March 14, 2019 | Article Type: Tutorial, Techdoc

Introduction



Concepts

Python

Python is an interpreted, high-level, general-purpose programming language. Created by Guido van Rossum and first released in 1991, Python has a design philosophy that emphasizes code readability, notably using significant whitespace. It provides constructs that enable clear programming on both small and large scales. Van Rossum led the language community until stepping down as leader in July 2018. Source: https://en.wikipedia.org/wiki/Python_(programming_language)

Python Pip

What is PIP?

PIP is a package manager for Python packages, or modules if you like.

What is a Package?

A package contains all the files you need for a module. Modules are Python code libraries you can include in your project. Source: https://www.w3schools.com/python/python_pip.asp

Flask

Flask is a micro web framework written in Python. It is classified as a microframework because it does not require particular tools or libraries. It has no database abstraction layer, form validation, or any other components where pre-existing third-party libraries provide common functions. However, Flask supports extensions that can add application features as if they were implemented in Flask itself. Extensions exist for object-relational mappers, form validation, upload handling, various open authentication technologies and several common framework related tools. Extensions are updated far more regularly than the core Flask program. Applications that use the Flask framework include Pinterest, LinkedIn, and the community web page for Flask itself. Source: https://en.wikipedia.org/wiki/Flask_(web_framework)

CSRF Protection

A common technique against CSRF attacks is to add a random string to the session, and check that string against a hidden field in the POST. Source: http://flask.pocoo.org/snippets/3/

Tools Needed

This guide requires you these things:

  1. Windows/Linux/MAC computer
  2. (Optional) Computer with at least a 900+ score in passmark or better and at least 2GB of ram or higher. This is to ensure a smooth experience, no one wants to code on a laggy machine right?
  3. Python, and Pip installed
  4. Mysql installed
  5. VS Code or any other IDE that floats your boat
  6. Basic knowledge in Python, HTML, and Mysql

Creating The Project



Flask Setup

For starters we need to install Flask using Pip. Of course we need to install the main star of the show, Flask. Just open up your command prompt (for Windows) or Terminal (for Linux/Mac) and type:

Pip install Flask

Yes! Now Flask is installed and good to go Get Started In A Simple Flask ‘Hello World’ Page To get started, create a folder for your project, any name will do. Let’s name it ‘helloworld’. Now on that folder create a folder named ‘templates’. This folder shall be where you will place all your HTML files. Now, open your favorite text editor or IDE (we recommend VS Code) and type out the following code:
from flask import Flask, render_template App = Flask(__name__)

Why we import Flask is pretty obvious, while importing render_template is for us to be able to render an HTML page. The next code should be put on the very last line of the flask code:
if __name__ = __main__:
  app.run()

The code above should always be on the line, save it as ‘blahblah.py’, can be any name you want. Next, we will create the HTML page inside the ‘templates’ folder. You can type any HTML file here you can even type out:
<html>Hello world</html>

Saved it as ‘index.html’ but it can be any name you would like. Next step is to create a route. You will need to assign routes so that flask knows what HTML page is rendered on a specific route. For routing, use below code:
@app.route(‘/index’) # it can be anything not just index but just make sure none of your routes have duplicates and your routes start with a ‘/’
def index(): # it can be anything but to avoid confusion we usually make it the same name as the route
return render_template(‘index.html’) # render_template means that it will render an HTML page you specified, in this case it’s index.html.

That’s it! Now let’s test out your flask app. Open up the terminal and “cd” your way and open your ‘helloworld’ folder. Now type:
python blahblah.py

Or whatever you named your py file. If there are no errors in your code, you should see something like:

Now, open up your favorite web browser and go to ‘localhost:5000/index’, so you notice that ‘/index’ is the route you set earlier. This is how you set routes in flask. After a few milliseconds of loading, surprise surprise! It’s that HTML you set in render_template for that app.route successfully ‘rendered’.
Small note: If you’re wondering if you can have more than two slashes in your routes, yes you can. You can set a ‘/index/index1/index2…’ in your route.



Database Setup

Now for more neat stuff, let’s add MySQL to the mix. Create a new database, create a new user, and grant all privileges on that user. For this tutorial let’s say you’ve used the following:
Database Name: database
User: user
Password: 12345
Open your flask py file, ‘blahblah.py’ and add:

from flask_sqlalchemy import SQLAlchemy #Always follow the caps that’s a big letter ‘SQL’ and ‘A’, python is quite case sensitive
app.config['SQLALCHEMY_DATABASE_URI'] = (yourdatabaseuri) #For your database URI, check out the URI doc here: link
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['SECRET_KEY'] = os.urandom(24)
db = SQLAlchemy(app)

Secret Key will be useful for later in the login form but for now let’s add it. Then you’re done! You’ve successfully added your database link but that’s only the tip of the iceberg, let’s keep going deeper.



Database Classes

Now let’s create your database class. This is your database columns.

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

Index is true means that the column username can be indexed while unique is true means that there can’t be a same value within that column. The id column is important as this is your primary_key. That’s it, now let’s create the columns in your mysql database. In your terminal:
python
from blahblah import db
db.create_all()

It’s successful if it did not return any errors and you can provide another input. Quit the python app
quit()



Registration Form

Phew, that was tiring and I guess it’s time for a coffee break. Don’t worry this guide will still be here after a cup of coffee (I suppose). Now that we’ve had coffee, we’re brimming with enery so let’s pick up the pace. Here’s the code for the registration form:

from flask_wtf import FlaskForm
from flask_wtf.csrf import CSRFError, CSRFProtect
from wtforms import StringField, PasswordField, validators, ValidationError, TextAreaField, HiddenField
class RegistrationForm(FlaskForm):
  username = StringField("Username", [validators.InputRequired(), validators.Length(min=4, max=20)])
  email = StringField("Email", [validators.InputRequired(), validators.Email()])
  password = PasswordField("New Password", [validators.InputRequired(), validators.EqualTo('confirm', message="Passwords must match")])
  confirm = PasswordField("Repeat Password")

@app.route('/register', methods=['GET', 'POST'])
def register():
  form = RegistrationForm()
  if request.method == 'POST' and form.validate_on_submit():
    user = User(username=form.username.data, password=form.password.data, email=form.email.data)
    db.session.add(user)
    db.session.commit()
    return redirect(url_for('login'))
  return render_template('registration.html', form=form)

In your Registration HTML feel free to download the below sample registration file here. CSRF protect is needed for CSRF protection.



Login Form

Now to create the login form. This can be done with Flask Login_Manager and some codes such as below:

class LoginForm(FlaskForm):
  def username_check(self, field):
    r = User.query.filter_by(username=self.username.data).first().username
    if r == [] or None:
      raise ValidationError('Invalid Username')
 def password_check(self, field):
    r = check_password_hash(User.query.filter_by(username=self.username.data).first().password, self.password.data)
  if r is False or not True or None:
    raise ValidationError('Invalid Password')
  username = StringField('username', [validators.InputRequired(), username_check])
  password = PasswordField('password', [validators.InputRequired(), password_check])

@app.route('/login', methods=['GET', 'POST'])
def login():
  form = LoginForm()
  if request.method == 'POST' and form.validate_on_submit():
    r = User.query.filter_by(username=form.username.data).first().id
    session['user_id'] = r
    resp = make_response(redirect (url_for('homepage')))
    resp.set_cookie('username', generate_unistring(24).encode('utf8'), max_age=(2*24*60*60))
    return resp
  return render_template('login.html',form=form)

@app.route('/logout')
@login_required
def logout():
  session.pop('user_id', None)
  resp = make_response(redirect (url_for('homepage')))
  resp.set_cookie('username', '', max_age=(2*24*60*60))
  return resp

To have a ‘@login_required’ wrap, use code below:
def login_required(fn):
@wraps(fn)
def wrapper(*args, **kwargs):
  if not session.get('user_id'):
    flash("Please log in first")
    return redirect(url_for('login'))
  return fn(*args, **kwargs)
return wrapper

Simply add this below any @app.route that you want only ‘logged in’ users to see
Now, you can check the code for the login HTML here.
We’re done! Add some CSS magic and you can flaunt or simply show it to your friends.