If you want to retrieve a registered user's information, it will be necessary for you to work with a database. You will be able to query the database to retrieve a user's information, and present it in a template.
You have a lot going on well for you above except for some minor modifications that you will need to do, and a few things you can add. I have started from the very beginning so you can follow through:
- Create a simple application to ensure that every part of flask is working
- Configure your application to work with web forms and a database
- Create a simple database (I will use
sqlite
. You can use any other if you want)
- Create registration and login form structures
- Display the forms in HTML templates
- Render those templates using view functions
Consider using the structure below when building your application. It is convenient to follow the principle of separation of concerns when building your application in that parts of your application are clustered in modules.
project
| --- flapp.py
| --- config.py
| --- .flaskenv
| --- app/
| --- __init__.py
| --- routes.py
| --- models.py
| --- forms.py
| --- templates/
| --- base.html
| --- home.html
| --- register.html
| --- login.html
| --- static/
1. Create an application instance and ensure the initial project is working
- Install
flask
, flask-wtf
, flask-login
, email-validator
, python-dotenv
in your virtual environment
Initialize these packages in __init__.py
file:
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from flask_login import LoginManager
from config import Config
app = Flask(__name__)
app.config.from_object(Config)
db = SQLAlchemy(app)
migrate = Migrate(app, db)
login = LoginManager(app)
login.login_view = 'login'
from app import routes, models
I have added login.login_view = 'login'
which will be used to protect the Home page from being accessed by anonymous users. I have also imported the Config
class from the config module which we will create in a subsequent section below.
# flapp.py
from app import app
This file acts as an entry point to your Flask application.
# routes.py
from app import app
@app.route('/')
@app.route('/home')
def home:
return 'home'
The home()
view function will return the text "home" when you run the application.
# .flaskenv
FLASK_APP=school.py
FLASK_ENV=development
FLASK_DEBUG=True
Flask expects these variables before starting the server. The package python-dotenv
installed earlier is used to load them instead of having to pass each variable in the terminal before typing flask run
.
When you run the application, you should see "Home".
2. Configure the application
Install flask-sqlalchemy
, flask-migrate
. They are needed to run and manage your database. Respective variables are initialized from os.environ.get
.
import os
basedir = os.path.abspath(os.path.dirname(__file__))
class Config(object):
SECRET_KEY = os.environ.get('SECRET_KEY') or 'difficult-to-guess'
SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or \
'sqlite:///' + os.path.join(basedir, 'app.db')
SQLALCHEMY_TRACK_MODIFICATIONS = False
Feel free to change the database to whichever you want. SECRET_KEY
is needed when you work with web forms.
3. Create a database
# models.py
from app import db, login
from werkzeug.security import generate_password_hash, check_password_hash
from flask_login import UserMixin
class User(UserMixin, db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(64), index=True, unique=True)
email = db.Column(db.String(120), index=True, unique=True)
password_hash = db.Column(db.String(128))
def __repr__(self):
return '<User {}>'.format(self.username)
def set_password(self, password):
self.password_hash = generate_password_hash(password)
def check_password(self, password):
return check_password_hash(self.password_hash, password)
@login.user_loader
def load_user(id):
return User.query.get(int(id))
To create this database, run these commands in your terminal:
# create a migration directory
(venv)$ flask db init
# Create a migration script
(venv)$ flask db migrate -m 'user table'
# Apply the changes
(venv)$ flask db upgrade
This will create a User
table with columns for username
, email
and a hashed password
. It is strongly advised not to store a user's password in the database, rather store it as a representation of itself in the form of a hash.
4. Simple form structure
The registration form will look like this:
# register.html
{% extends "base.html" %}
{% block content %}
<h1>Register</h1>
<form action="" method="post">
{{ form.hidden_tag() }}
<p>
{{ form.username.label }}<br>
{{ form.username(size=32) }}<br>
{% for error in form.username.errors %}
<span style="color: red;">[{{ error }}]</span>
{% endfor %}
</p>
<p>
{{ form.email.label }}<br>
{{ form.email(size=32) }}<br>
{% for error in form.email.errors %}
<span style="color: red;">[{{ error }}]</span>
{% endfor %}
</p>
<p>
{{ form.password.label }}<br>
{{ form.password(size=32) }}<br>
{% for error in form.password.errors %}
<span style="color: red;">[{{ error }}]</span>
{% endfor %}
</p>
<p>
{{ form.password2.label }}<br>
{{ form.password2(size=32) }}<br>
{% for error in form.password2.errors %}
<span style="color: red;">[{{ error }}]</span>
{% endfor %}
</p>
<p>{{ form.submit() }}</p>
</form>
{% endblock %}
The login form will look like this:
# login.html
{% extends "base.html" %}
{% block content %}
<h1>Sign In</h1>
<form action="" method="post" novalidate>
{{ form.hidden_tag() }}
<p>
{{ form.username.label }}<br>
{{ form.username(size=32) }}<br>
{% for error in form.username.errors %}
<span style="color: red;">[{{ error }}]</span>
{% endfor %}
</p>
<p>
{{ form.password.label }}<br>
{{ form.password(size=32) }}<br>
{% for error in form.password.errors %}
<span style="color: red;">[{{ error }}]</span>
{% endfor %}
</p>
<p>{{ form.remember_me() }} {{ form.remember_me.label }}</p>
<p>{{ form.submit() }}</p>
</form>
<p>New here? <a href="{{ url_for('register') }}">Register now!</a></p>
{% endblock %}
Both these templates inherit the structure of the base
template using the keyword extends
. So, this base.html file will look like this:
# base.html
<html>
<head>
{% if title %}
<title>{{ title }} - demo</title>
{% else %}
<title>so demo</title>
{% endif %}
</head>
<body>
<div>
SO Demo:
<a href="{{ url_for('home') }}">Home</a>
{% if current_user.is_anonymous %}
<a href="{{ url_for('login') }}">Login</a>
{% else %}
<a href="{{ url_for('logout') }}">Logout</a>
{% endif %}
</div>
<hr>
{% with messages = get_flashed_messages() %}
{% if messages %}
<ul>
{% for message in messages %}
<li>{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
{% endwith %}
{% block content %}{% endblock %}
</body>
</html>
Every time a user tries to register or log in to their accounts, a flash message will appear. This is shown in the base template.
The home page will look like this:
# home.html
{% extends "base.html" %}
{% block content %}
<p>
Hi {{ current_user.username }},<br><br>
These are your details:
</p>
<p>
Name: {{ current_user.username }}<br>
Email: {{ current_user.email }}<br>
Hashed Password: {{ current_user.password_hash }}
</p>
{% endblock %}
We display the current_user
's details as stored in the database.
The view functions that handle all this information includes the home()
, register()
, login()
, and additionally logout()
as seen here:
from flask import render_template, flash, redirect, url_for, request
from app import app, db
from app.forms import LoginForm, RegistrationForm
from flask_login import current_user, login_user, logout_user
from app.models import User
from flask_login import login_required
from werkzeug.urls import url_parse
@app.route('/')
@app.route('/home')
@login_required
def home():
return render_template('home.html')
@app.route('/login', methods=['GET', 'POST'])
def login():
if current_user.is_authenticated:
return redirect(url_for('index'))
form = LoginForm()
if form.validate_on_submit():
user = User.query.filter_by(username=form.username.data).first()
if user is None or not user.check_password(form.password.data):
flash('Invalid username or password')
return redirect(url_for('login'))
login_user(user, remember=form.remember_me.data)
next_page = request.args.get('next')
if not next_page or url_parse(next_page).netloc != '':
next_page = url_for('home')
return redirect(next_page)
return render_template('login.html', title='Sign In', form=form)
@app.route('/logout')
def logout():
logout_user()
return redirect(url_for('home'))
@app.route('/register', methods=['GET', 'POST'])
def register():
if current_user.is_authenticated:
return redirect(url_for('home'))
form = RegistrationForm()
if form.validate_on_submit():
user = User(username=form.username.data, email=form.email.data)
user.set_password(form.password.data)
db.session.add(user)
db.session.commit()
flash('Congratulations, you are now a registered user!')
return redirect(url_for('login'))
return render_template('register.html', title='Register', form=form)
You should have this:
