Basic App¶
• Intro • QuickStart App • Basic App • MongoDB App
The Basic App builds on the QuickStart App by adding the following features:
- Register and Login with an email
- Confirm emails, Change passwords
- Role-base authorization
- Enable translations
Unlike the QuickStart App, the Basic App requires proper SMTP settings
and the installation of Flask-BabelEx
.
Create a development environment¶
We recommend making use of virtualenv and virtualenvwrapper:
# Create virtual env
mkvirtualenv my_app
workon my_app
# Create working directory
mkdir -p ~dev/my_app # or mkdir C:\dev\my_app
cd ~/dev/my_app # or cd C:\dev\my_app
Install required Python packages¶
# Uninstall Flask-Babel if needed
pip uninstall Flask-Babel
# Install Flask-BabelEx and Flask-User
pip install Flask-BabelEx
pip install Flask-User
Create the basic_app.py file¶
- Open your favorite editor,
- Copy the example below, and
- Save it as ~/dev/my_app/basic_app.py
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 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 | # This file contains an example Flask-User application.
# To keep the example simple, we are applying some unusual techniques:
# - Placing everything in one file
# - Using class-based configuration (instead of file-based configuration)
# - Using string-based templates (instead of file-based templates)
import datetime
from flask import Flask, request, render_template_string
from flask_babelex import Babel
from flask_sqlalchemy import SQLAlchemy
from flask_user import current_user, login_required, roles_required, UserManager, UserMixin
# Class-based application configuration
class ConfigClass(object):
""" Flask application config """
# Flask settings
SECRET_KEY = 'This is an INSECURE secret!! DO NOT use this in production!!'
# Flask-SQLAlchemy settings
SQLALCHEMY_DATABASE_URI = 'sqlite:///basic_app.sqlite' # File-based SQL database
SQLALCHEMY_TRACK_MODIFICATIONS = False # Avoids SQLAlchemy warning
# Flask-Mail SMTP server settings
MAIL_SERVER = 'smtp.gmail.com'
MAIL_PORT = 465
MAIL_USE_SSL = True
MAIL_USE_TLS = False
MAIL_USERNAME = 'email@example.com'
MAIL_PASSWORD = 'password'
MAIL_DEFAULT_SENDER = '"MyApp" <noreply@example.com>'
# Flask-User settings
USER_APP_NAME = "Flask-User Basic App" # Shown in and email templates and page footers
USER_ENABLE_EMAIL = True # Enable email authentication
USER_ENABLE_USERNAME = False # Disable username authentication
USER_EMAIL_SENDER_NAME = USER_APP_NAME
USER_EMAIL_SENDER_EMAIL = "noreply@example.com"
def create_app():
""" Flask application factory """
# Create Flask app load app.config
app = Flask(__name__)
app.config.from_object(__name__+'.ConfigClass')
# Initialize Flask-BabelEx
babel = Babel(app)
# Initialize Flask-SQLAlchemy
db = SQLAlchemy(app)
# Define the User data-model.
# NB: Make sure to add flask_user UserMixin !!!
class User(db.Model, UserMixin):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
active = db.Column('is_active', db.Boolean(), nullable=False, server_default='1')
# User authentication information. The collation='NOCASE' is required
# to search case insensitively when USER_IFIND_MODE is 'nocase_collation'.
email = db.Column(db.String(255, collation='NOCASE'), nullable=False, unique=True)
email_confirmed_at = db.Column(db.DateTime())
password = db.Column(db.String(255), nullable=False, server_default='')
# User information
first_name = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
last_name = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
# Define the relationship to Role via UserRoles
roles = db.relationship('Role', secondary='user_roles')
# Define the Role data-model
class Role(db.Model):
__tablename__ = 'roles'
id = db.Column(db.Integer(), primary_key=True)
name = db.Column(db.String(50), unique=True)
# Define the UserRoles association table
class UserRoles(db.Model):
__tablename__ = 'user_roles'
id = db.Column(db.Integer(), primary_key=True)
user_id = db.Column(db.Integer(), db.ForeignKey('users.id', ondelete='CASCADE'))
role_id = db.Column(db.Integer(), db.ForeignKey('roles.id', ondelete='CASCADE'))
# Setup Flask-User and specify the User data-model
user_manager = UserManager(app, db, User)
# Create all database tables
db.create_all()
# Create 'member@example.com' user with no roles
if not User.query.filter(User.email == 'member@example.com').first():
user = User(
email='member@example.com',
email_confirmed_at=datetime.datetime.utcnow(),
password=user_manager.hash_password('Password1'),
)
db.session.add(user)
db.session.commit()
# Create 'admin@example.com' user with 'Admin' and 'Agent' roles
if not User.query.filter(User.email == 'admin@example.com').first():
user = User(
email='admin@example.com',
email_confirmed_at=datetime.datetime.utcnow(),
password=user_manager.hash_password('Password1'),
)
user.roles.append(Role(name='Admin'))
user.roles.append(Role(name='Agent'))
db.session.add(user)
db.session.commit()
# The Home page is accessible to anyone
@app.route('/')
def home_page():
return render_template_string("""
{% extends "flask_user_layout.html" %}
{% block content %}
<h2>{%trans%}Home page{%endtrans%}</h2>
<p><a href={{ url_for('user.register') }}>{%trans%}Register{%endtrans%}</a></p>
<p><a href={{ url_for('user.login') }}>{%trans%}Sign in{%endtrans%}</a></p>
<p><a href={{ url_for('home_page') }}>{%trans%}Home Page{%endtrans%}</a> (accessible to anyone)</p>
<p><a href={{ url_for('member_page') }}>{%trans%}Member Page{%endtrans%}</a> (login_required: member@example.com / Password1)</p>
<p><a href={{ url_for('admin_page') }}>{%trans%}Admin Page{%endtrans%}</a> (role_required: admin@example.com / Password1')</p>
<p><a href={{ url_for('user.logout') }}>{%trans%}Sign out{%endtrans%}</a></p>
{% endblock %}
""")
# The Members page is only accessible to authenticated users
@app.route('/members')
@login_required # Use of @login_required decorator
def member_page():
return render_template_string("""
{% extends "flask_user_layout.html" %}
{% block content %}
<h2>{%trans%}Members page{%endtrans%}</h2>
<p><a href={{ url_for('user.register') }}>{%trans%}Register{%endtrans%}</a></p>
<p><a href={{ url_for('user.login') }}>{%trans%}Sign in{%endtrans%}</a></p>
<p><a href={{ url_for('home_page') }}>{%trans%}Home Page{%endtrans%}</a> (accessible to anyone)</p>
<p><a href={{ url_for('member_page') }}>{%trans%}Member Page{%endtrans%}</a> (login_required: member@example.com / Password1)</p>
<p><a href={{ url_for('admin_page') }}>{%trans%}Admin Page{%endtrans%}</a> (role_required: admin@example.com / Password1')</p>
<p><a href={{ url_for('user.logout') }}>{%trans%}Sign out{%endtrans%}</a></p>
{% endblock %}
""")
# The Admin page requires an 'Admin' role.
@app.route('/admin')
@roles_required('Admin') # Use of @roles_required decorator
def admin_page():
return render_template_string("""
{% extends "flask_user_layout.html" %}
{% block content %}
<h2>{%trans%}Admin Page{%endtrans%}</h2>
<p><a href={{ url_for('user.register') }}>{%trans%}Register{%endtrans%}</a></p>
<p><a href={{ url_for('user.login') }}>{%trans%}Sign in{%endtrans%}</a></p>
<p><a href={{ url_for('home_page') }}>{%trans%}Home Page{%endtrans%}</a> (accessible to anyone)</p>
<p><a href={{ url_for('member_page') }}>{%trans%}Member Page{%endtrans%}</a> (login_required: member@example.com / Password1)</p>
<p><a href={{ url_for('admin_page') }}>{%trans%}Admin Page{%endtrans%}</a> (role_required: admin@example.com / Password1')</p>
<p><a href={{ url_for('user.logout') }}>{%trans%}Sign out{%endtrans%}</a></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)
|
- Lines 25-32 configure
Flask-Mail
. Make sure to use the correct settings or emails - will not be sent.
- Lines 25-32 configure
Configure Flask-Mail¶
Make sure to properly configure Flask-Mail settings:
# Flask-Mail SMTP Server settings
MAIL_SERVER =
MAIL_PORT =
MAIL_USE_SSL =
MAIL_USE_TLS =
# Flask-Mail SMTP Account settings
MAIL_USERNAME =
MAIL_PASSWORD =
Note
Gmail and Yahoo mail nowadays disable SMTP requests by default. Search the web for ‘Gmail less secure apps’ or ‘Yahoo less secure apps’ to learn how to change this default setting for your account.
See QuickStart App for an example without SMTP.
Configure your browser¶
If you want to see translation in action, you will need to change and prioritize a supported language (one that is other than ‘English’) in your browser language preferences.
For Google Chrome:
- Chrome > Preferences. Search for ‘Language’.
- Expand the ‘Language’ bar > Add languages.
- Check the ‘Dutch’ checkbox > Add.
- Make sure to move it to the top: Three dots > Move to top.
Run the Basic App¶
Run the Basic App with the following command:
cd ~/dev/my_app
python basic_app.py
And point your browser to http://localhost:5000
.
Troubleshooting¶
If you receive an EmailError message, or if the Registration form does not respond quickly then you may have specified incorrect SMTP settings.
If you receive a SQLAlchemy error message, you may be using an old DB schema. Delete the quickstart_app.sqlite file and restart the app.
If you don’t see any translations, you may not have installed Flask-BabelEx
,
or you may not have prioritized a supported language in your browser settings.
• Intro • QuickStart App • Basic App • MongoDB App