Flask is a minimalist Python framework, that possibilities high customization in your Design Patterns.
Assuming that our project already has your database connection already configured we will start with the following configuration:
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_script import Manager
from flask_migrate import Migrate, MigrateCommand
from flask_login import LoginManager
app = Flask(__name__)
app.config.from_object('config')
login_manager = LoginManager(app)
db = SQLAlchemy(app)
migrate = Migrate(app, db)
manager = Manager(app)
manager.add_command('db', MigrateCommand)
from app.models import tables, forms
from app.controllers import default
Our forms:
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, BooleanField
from wtforms.validators import DataRequired, Email
class LoginForm(FlaskForm):
username = StringField("username", validators=[DataRequired()])
password = PasswordField("password", validators=[DataRequired()])
remember_me = BooleanField()
class RegisterForm(FlaskForm):
username = StringField("username", validators=[DataRequired()])
password = PasswordField("password", validators=[DataRequired()])
name = StringField("name")
email = StringField("email", validators=[DataRequired(), Email()])
our tables:
from app import db, login_manager
from flask_login import UserMixin
from werkzeug.security import generate_password_hash, check_password_hash
@login_manager.user_loader
def get_user(user_id):
return User.query.get(user_id)
class User(db.Model, UserMixin):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True, nullable=False)
password = db.Column(db.String(80))
name = db.Column(db.String(80))
email = db.Column(db.String(120), unique=True, nullable=False)
def __init__(self, username, password, name, email):
self.username = username
self.password = generate_password_hash(password)
self.name = name
self.email = email
def __repr__(self):
return f'<User {self.username}>'
def verify_password(self, pwd):
return check_password_hash(self.password, pwd)
In the example above we are making the User model extends UserMixin so that we don’t need implement the functions is_authenticated, is_active, is_anonymous and get_id, necessary to implement of login.
In this case I use the native werkzeug lib of python to generate the hash in the User model constructor and the password verification with the check_password_hash method that returns true if password ok.
In controllers we have the functions (index, register, login e logout):
Register controller:
...
@app.route('/register', methods=['GET', 'POST'])
def register():
form = RegisterForm()
username = form.username.data
password = form.password.data
name = form.name.data
email = form.email.data
if form.validate_on_submit():
user = User.query.filter_by(username=username).first()
if not user:
user = User(username, password, name, email)
db.session.add(user)
db.session.commit()
return redirect(url_for('login'))
else:
return render_template('register.html', form=form)
In Register controller, we use the data provided by the user to create a new user in the system.
Login controller:
...
@app.route("/login", methods=["GET", "POST"])
def login():
form = LoginForm()
username = form.username.data
password = form.password.data
if form.validate_on_submit():
user = User.query.filter_by(username=username).first()
if user and user.verify_password(password):
login_user(user)
flash("Usuário Logado!")
else:
flash("Login ivalido!")
else:
print(form.errors)
return render_template('login.html', form=form)
In Login controller, we use the username and password provided by the user to authenticate him with the flask_login method login_user and we send a message if the user is authenticated or not.
Index controller:
...
@app.route("/")
@login_required
def index():
return render_template('index.html')
In Index controller, the decorator @login_required was used so that only logged-in users can access the main page
Logout controller:
...
@app.route('/logout')
def logout():
logout_user()
return redirect(url_for('login'))
In Logout controller, the current user logged-in in session is logged out with the flask_login method logout_user and a redirection is made to the login page.
You can see the full project in my github.