Basic App

The sample code below illustrates the power of using Flask-User with sensible defaults: With just a dozen additional code statements, a basic Flask application can be transformed to offer the following features:

  • Register with username and email
  • Email confirmation
  • Login with username or email, Logout
  • Protect pages from unauthenticated access
  • Change username
  • Change password
  • Forgot password

Single-file techniques

To keep the examples simple, we are using some unusual single-file techniques:
- Using class based configuration instead of file based configuration
- Using render_template_string() instead of render_template()
- Placing everything in one file

None of these techniques are recommended outside of tutorial usage.

Setup a development environment

These tutorials assume that you are working with virtualenv and virtualenvwrapper and that the code resides in ~/dev/example:

# Create virtualenv 'example'
mkvirtualenv example

# Install required Python packages in the 'example' virtualenv
workon example
pip install flask-user
pip install flask-mail

# Change working directory
mkdir -p ~dev/example
cd ~/dev/example                # or C:\dev\example on Windows

Create the basic_app.py file

Create ~/dev/example/basic_app.py with the content below.

Make sure to replace the following settings:
MAIL_USERNAME = ‘email@example.com
MAIL_PASSWORD = ‘password’
MAIL_DEFAULT_SENDER = ‘“Sender” <noreply@example.com>’
MAIL_SERVER = ‘smtp.gmail.com’
MAIL_PORT = 465
MAIL_USE_SSL = True
MAIL_USE_TLS = False
with settings that are appropriate for your SMTP server.

Highlighted lines shows the lines added to a basic Flask application.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
import os
from flask import Flask, render_template_string
from flask_babel import Babel
from flask_mail import Mail
from flask_sqlalchemy import SQLAlchemy
from flask_user import login_required, UserManager, UserMixin, SQLAlchemyAdapter


# Use a Class-based config to avoid needing a 2nd file
# os.getenv() enables configuration through OS environment variables
class ConfigClass(object):
    # Flask settings
    SECRET_KEY =              os.getenv('SECRET_KEY',       'THIS IS AN INSECURE SECRET')
    SQLALCHEMY_DATABASE_URI = os.getenv('DATABASE_URL',     'sqlite:///basic_app.sqlite')
    CSRF_ENABLED = True

    # Flask-Mail settings
    MAIL_USERNAME =           os.getenv('MAIL_USERNAME',        'youremail@example.com')
    MAIL_PASSWORD =           os.getenv('MAIL_PASSWORD',        'yourpassword')
    MAIL_DEFAULT_SENDER =     os.getenv('MAIL_DEFAULT_SENDER',  '"MyApp" <noreply@example.com>')
    MAIL_SERVER =             os.getenv('MAIL_SERVER',          'smtp.gmail.com')
    MAIL_PORT =           int(os.getenv('MAIL_PORT',            '465'))
    MAIL_USE_SSL =        int(os.getenv('MAIL_USE_SSL',         True))

    # Flask-User settings
    USER_APP_NAME        = "AppName"                # Used by email templates


def create_app():
    """ Flask application factory """
    
    # Setup Flask app and app.config
    app = Flask(__name__)
    app.config.from_object(__name__+'.ConfigClass')

    # Initialize Flask-BabelEx
    babel = Babel(app)

    # Initialize Flask extensions
    db = SQLAlchemy(app)                            # Initialize Flask-SQLAlchemy
    mail = Mail(app)                                # Initialize Flask-Mail

    # Define the User data model.
    # Make sure to add flask_user UserMixin !!!
    class User(db.Model, UserMixin):
        id = db.Column(db.Integer, primary_key=True)

        # User authentication information
        username = db.Column(db.String(50), nullable=False, unique=True)
        password = db.Column(db.String(255), nullable=False, server_default='')

        # User email information
        email = db.Column(db.String(255), nullable=False, unique=True)
        confirmed_at = db.Column(db.DateTime())

        # User information
        active = db.Column('is_active', db.Boolean(), nullable=False, server_default='0')
        first_name = db.Column(db.String(100), nullable=False, server_default='')
        last_name = db.Column(db.String(100), nullable=False, server_default='')

    # Create all database tables
    db.create_all()

    # Setup Flask-User
    db_adapter = SQLAlchemyAdapter(db, User)        # Register the User model
    user_manager = UserManager(db_adapter, app)     # Initialize Flask-User

    # The Home page is accessible to anyone
    @app.route('/')
    def home_page():
        return render_template_string("""
            {% extends "base.html" %}
            {% block content %}
                <h2>Home page</h2>
                <p>This page can be accessed by anyone.</p><br/>
                <p><a href={{ url_for('home_page') }}>Home page</a> (anyone)</p>
                <p><a href={{ url_for('members_page') }}>Members page</a> (login required)</p>
            {% endblock %}
            """)

    # The Members page is only accessible to authenticated users
    @app.route('/members')
    @login_required                                 # Use of @login_required decorator
    def members_page():
        return render_template_string("""
            {% extends "base.html" %}
            {% block content %}
                <h2>Members page</h2>
                <p>This page can only be accessed by authenticated users.</p><br/>
                <p><a href={{ url_for('home_page') }}>Home page</a> (anyone)</p>
                <p><a href={{ url_for('members_page') }}>Members page</a> (login required)</p>
            {% endblock %}
            """)

    return app


# Start development web server
if __name__=='__main__':
    app = create_app()
    app.run(host='0.0.0.0', port=5000, debug=True)

Run the Basic App

Run the Basic App with the following command:

cd ~/dev/example
python basic_app.py

And point your browser to http://localhost:5000.

Troubleshooting

If you receive an SendEmailError message, or if the Registration form does not respond quickly then you may have specified incorrect SMTP settings.

If you receive a ‘AssertionError: No sender address has been set’ error, you may be using an old version of Flask-Mail which uses DEFAULT_MAIL_SENDER instead of MAIL_DEFAULT_SENDER.

If you receive a SQLAlchemy error message, delete the basic_app.sqlite file and restart the app. You may be using an old DB schema in that file.