Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
Flask-Security and Flask-Admin example by Steve Saporata
# Example of combining Flask-Security and Flask-Admin.
# by Steve Saporta
# April 15, 2014
# Uses Flask-Security to control access to the application, with "admin" and "end-user" roles.
# Uses Flask-Admin to provide an admin UI for the lists of users and roles.
# SQLAlchemy ORM, Flask-Mail and WTForms are used in supporting roles, as well.
from flask import Flask, render_template
from flask.ext.sqlalchemy import SQLAlchemy
from import current_user, login_required, RoleMixin, Security, \
SQLAlchemyUserDatastore, UserMixin, utils
from flask_mail import Mail
from flask.ext.admin import Admin
from flask.ext.admin.contrib import sqla
from wtforms.fields import PasswordField
# Initialize Flask and set some config values
app = Flask(__name__)
# Replace this with your own secret key
app.config['SECRET_KEY'] = 'super-secret'
# The database must exist (although it's fine if it's empty) before you attempt to access any page of the app
# in your browser.
# I used a PostgreSQL database, but you could use another type of database, including an in-memory SQLite database.
# You'll need to connect as a user with sufficient privileges to create tables and read and write to them.
# Replace this with your own database connection string.
app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql://postgres:xxxxxxxx@localhost/flask_example'
# Set config values for Flask-Security.
# We're using PBKDF2 with salt.
app.config['SECURITY_PASSWORD_HASH'] = 'pbkdf2_sha512'
# Replace this with your own salt.
app.config['SECURITY_PASSWORD_SALT'] = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
# Flask-Security optionally sends email notification to users upon registration, password reset, etc.
# It uses Flask-Mail behind the scenes.
# Set mail-related config values.
# Replace this with your own "from" address
app.config['SECURITY_EMAIL_SENDER'] = ''
# Replace the next five lines with your own SMTP server settings
app.config['MAIL_SERVER'] = ''
app.config['MAIL_PORT'] = 465
app.config['MAIL_USE_SSL'] = True
app.config['MAIL_USERNAME'] = 'xxxxxxxxxxxxxxxxxxxx'
app.config['MAIL_PASSWORD'] = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
# Initialize Flask-Mail and SQLAlchemy
mail = Mail(app)
db = SQLAlchemy(app)
# Create a table to support a many-to-many relationship between Users and Roles
roles_users = db.Table(
db.Column('user_id', db.Integer(), db.ForeignKey('')),
db.Column('role_id', db.Integer(), db.ForeignKey(''))
# Role class
class Role(db.Model, RoleMixin):
# Our Role has three fields, ID, name and description
id = db.Column(db.Integer(), primary_key=True)
name = db.Column(db.String(80), unique=True)
description = db.Column(db.String(255))
# __str__ is required by Flask-Admin, so we can have human-readable values for the Role when editing a User.
# If we were using Python 2.7, this would be __unicode__ instead.
def __str__(self):
# __hash__ is required to avoid the exception TypeError: unhashable type: 'Role' when saving a User
def __hash__(self):
return hash(
# User class
class User(db.Model, UserMixin):
# Our User has six fields: ID, email, password, active, confirmed_at and roles. The roles field represents a
# many-to-many relationship using the roles_users table. Each user may have no role, one role, or multiple roles.
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(255), unique=True)
password = db.Column(db.String(255))
active = db.Column(db.Boolean())
confirmed_at = db.Column(db.DateTime())
roles = db.relationship(
backref=db.backref('users', lazy='dynamic')
# Initialize the SQLAlchemy data store and Flask-Security.
user_datastore = SQLAlchemyUserDatastore(db, User, Role)
security = Security(app, user_datastore)
# Executes before the first request is processed.
def before_first_request():
# Create any database tables that don't exist yet.
# Create the Roles "admin" and "end-user" -- unless they already exist
user_datastore.find_or_create_role(name='admin', description='Administrator')
user_datastore.find_or_create_role(name='end-user', description='End user')
# Create two Users for testing purposes -- unless they already exists.
# In each case, use Flask-Security utility function to encrypt the password.
encrypted_password = utils.encrypt_password('password')
if not user_datastore.get_user(''):
user_datastore.create_user(email='', password=encrypted_password)
if not user_datastore.get_user(''):
user_datastore.create_user(email='', password=encrypted_password)
# Commit any database changes; the User and Roles must exist before we can add a Role to the User
# Give one User has the "end-user" role, while the other has the "admin" role. (This will have no effect if the
# Users already have these Roles.) Again, commit any database changes.
user_datastore.add_role_to_user('', 'end-user')
user_datastore.add_role_to_user('', 'admin')
# Displays the home page.
# Users must be authenticated to view the home page, but they don't have to have any particular role.
# Flask-Security will display a login form if the user isn't already authenticated.
def index():
return render_template('index.html')
# Customized User model for SQL-Admin
class UserAdmin(sqla.ModelView):
# Don't display the password on the list of Users
column_exclude_list = list = ('password',)
# Don't include the standard password field when creating or editing a User (but see below)
form_excluded_columns = ('password',)
# Automatically display human-readable names for the current and available Roles when creating or editing a User
column_auto_select_related = True
# Prevent administration of Users unless the currently logged-in user has the "admin" role
def is_accessible(self):
return current_user.has_role('admin')
# On the form for creating or editing a User, don't display a field corresponding to the model's password field.
# There are two reasons for this. First, we want to encrypt the password before storing in the database. Second,
# we want to use a password field (with the input masked) rather than a regular text field.
def scaffold_form(self):
# Start with the standard form as provided by Flask-Admin. We've already told Flask-Admin to exclude the
# password field from this form.
form_class = super(UserAdmin, self).scaffold_form()
# Add a password field, naming it "password2" and labeling it "New Password".
form_class.password2 = PasswordField('New Password')
return form_class
# This callback executes when the user saves changes to a newly-created or edited User -- before the changes are
# committed to the database.
def on_model_change(self, form, model, is_created):
# If the password field isn't blank...
if len(model.password2):
# ... then encrypt the new password prior to storing it in the database. If the password field is blank,
# the existing password in the database will be retained.
model.password = utils.encrypt_password(model.password2)
# Customized Role model for SQL-Admin
class RoleAdmin(sqla.ModelView):
# Prevent administration of Roles unless the currently logged-in user has the "admin" role
def is_accessible(self):
return current_user.has_role('admin')
# Initialize Flask-Admin
admin = Admin(app)
# Add Flask-Admin views for Users and Roles
admin.add_view(UserAdmin(User, db.session))
admin.add_view(RoleAdmin(Role, db.session))
# If running locally, listen on all IP addresses, port 8080
if __name__ == '__main__':

This comment has been minimized.

Copy link

steve-swipetospin commented Feb 27, 2017

I'm happy you found my example helpful! Would you consider correcting the spelling of my name (Saporta) and linking to my original version?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.