Boilerplate Flask Apps

Reference codebase used in all Flask Apps provided by the AppSeed platform

All Flask Apps generated by AppSeed share the same codebase structure and features:

  • Reference Codebase - Flask Apps Boilerplate

  • SQLite database, Flask-SQLAlchemy ORM

  • Session-Based auth flow (login, register)

  • Deployment scripts: Docker, Gunicorn / Nginx, Heroku

Links

Support (Email and LIVE on Discord) for registered AppSeed users.

Environment

To use the stater, Python3 should be installed properly in the workstation. If you are not sure if Python is properly installed, please open a terminal and type python --version. The full list with dependencies and tools required to build the app:

  • Python3 - the programming language used to code the app

  • GIT - used to clone the source code from the Github repository

  • Basic development tools (g++ compiler, python development libraries ..etc) used by Python to compile the app dependencies in your environment.

Build the app

To built and start the app locally, follow the steps:

Get the source code

  • Download the ZIP from Github Repository

  • Using GIT tool in the terminal to clone the source code

Change the current directory to source code directory

$ # Make sure you are running the commands INSIDE source code directory
$
$ # Create and activate a Virtualenv (Unix based systems)
$ virtualenv env
$ source env/bin/activate
$
$ # Create and activate a Virtualenv (Windows based systems)
$ # virtualenv env
$ # .\env\Scripts\activate
$
$ # Install requirements
$ pip3 install -r requirements.txt
$
$ # Set the FLASK_APP environment variable
$ (Unix/Mac) export FLASK_APP=run.py
$ (Windows) set FLASK_APP=run.py
$ (Powershell) $env:FLASK_APP = ".\run.py"
$
$ # Set up the DEBUG environment
$ # (Unix/Mac) export FLASK_ENV=development
$ # (Windows) set FLASK_ENV=development
$ # (Powershell) $env:FLASK_ENV = "development"
$
$ # Run the application
$ # --host=0.0.0.0 - expose the app on all network interfaces (default 127.0.0.1)
$ # --port=5000 - specify the app port (default 5000)
$ flask run --host=0.0.0.0 --port=5000
$
$ # Access the app in browser: http://127.0.0.1:5000/

At this point, we can visit the app in the browser http://127.0.0.1:5000/. By default, the app will redirect guest users to the login page. To access the private pages:

  • Create a new user using the registration page

  • Authenticate using the login page

App Codebase (simplified)

Starter uses a simple codebase (no Blueprints) with a structure presented below:

< PROJECT ROOT >
|
|-- app/ # The actual APP
|-- app/__init__.py # App Constructor
|
|-- requirements.txt # App dependencies
|
|-- run.py # App Bootstraper (returs the WSGI app)
|
|-- ***********************************

The bootstrap flow

  • run.py imports the WSGI Application from app directory

  • app/__init__.py is called and trigger the following actions:

    • invoke Flask Framework constructor

    • Read the configuration from app/config.py

    • Invoke SQLAlchemy

    • Invoke LoginManager exposed by Flask-Login

app/__init__.py (simplified version)

# File: /app/__init__.py
from flask import Flask # Import Flask
from flask_sqlalchemy import SQLAlchemy # For ORM db access
from flask_login import LoginManager # Used for authentication
# Directive 1 - Inject Flask magic
app = Flask(__name__)
# Directive 2 - Load the configuraton from `app/config.py`
app.config.from_object('app.config.Config')
# Directive 3 - Flask-SqlAlchemy wrapping
db = SQLAlchemy (app)
# Directive 4 - Invoke and init the login manager
lm = LoginManager( )
lm.init_app(app)
# Directive 5 - Setup database (create tables)
@app.before_first_request
def initialize_database():
db.create_all()
# Directive 5 - Start the App
from app import views, models
# At this point we have a valid WSGI app

The app/__init__.py constructs the app by putting together a short-list of things:

  • Invoke Flask

  • Load the configuration from config.py

  • Invoke the SqlAlchemy ORM to handle the database content

  • Invoke and inject the Login Manager into the app

  • Check if the database tables are created

  • Finally, expose the app by importing views (app routing) and models (app tables)

App Codebase

The codebase structure is presented below:

< PROJECT ROOT >
|
|-- app/
| |-- __init__.py # App initializer
| |-- config.py # App configuration
| |-- forms.py # App Forms
| |-- models.py # App Models
| |-- util.py # Helpers
| |-- views.py # App Routing
| |
| |-- static/
| | |-- <css, JS, images> # CSS files, Javascripts files
| |
| |-- templates/
| | |
| | |-- includes/ # Page chunks, components
| | | |
| | | |-- navigation.html # Top bar
| | | |-- sidebar.html # Left sidebar
| | | |-- scripts.html # JS scripts common to all pages
| | | |-- footer.html # The common footer
| | |
| | |-- layouts/ # App Layouts (the master pages)
| | | |
| | | |-- base.html # Used by common pages like index, UI
| | | |-- base-fullscreen.html # Used by auth pages (login, register)
| | |
| | |-- accounts/ # Auth Pages (login, register)
| | | |
| | | |-- login.html # Use layout `base-fullscreen.html`
| | | |-- register.html # Use layout `base-fullscreen.html`
| | |
| | index.html # The default page
| | page-404.html # Error 404 page (page not found)
| | page-500.html # Error 500 page (server error)
| | *.html # All other pages provided by the UI Kit
|
|-- requirements.txt
|
|-- run.py
|
|-- ************************************************************************

App Configuration

The configuration file app/config.py specify a short-list with variables:

This configuration is loaded by app/__init__.py during the app initialization:

# File: /app/__init__.py
...
# Directive 2 - Load the configuraton from `app/config.py`
app.config.from_object('app.config.Config')
...

The default database is SQLite, configured by the SQLALCHEMY_DATABASE_URI variable:

# File: /app/config.py
...
SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(basedir, 'db.sqlite3')
SQLALCHEMY_TRACK_MODIFICATIONS = False
...

The database and associated tables, defined in app/models.py are automatically created at the first request in the physical file specified in the configuration - default location app/db.sqlite3.

Hint: to visualize the SQLite database content an external tool should be installed: DB Browser for SQLite it might be a good choice.

App Tables

The file app/models.py defines the table(s) used by the application. Being a simple starter, by default the following tabes are defined:

  • Table #1 - User with fields:

    • Id - Primary key, unique

    • user - Store the username

    • email - The email address

    • password - Hashed password

App Forms

The file app/forms.py defines the table(s) used by the application. Being a simple starter, by default the following forms are defined:

  • Form #1 - LoginForm with fields:

    • username

    • password

  • Form #2 - RegisterForm with fields:

    • name - The friendly name

    • username - used to authenticate

    • password - used to authenticate

    • email

App Routing

The file app/views.py defines the application routing. Here is the list with defined paths and associated handlers

Route /login.html

The route handles the app authentication using a simple flow:

  • Loads LoginForm defined in app/forms.py

  • GET request

    • loads the page app/tempates/accounts/login.html

  • POST request

    • Validate the input data: username, password

    • Locate the user in the database

    • Verify the password against the database version

    • For success, authenticate the user

    • For errors, reload the login page and inform the user

Route /register.html

The route handles the app authentication using a simple flow:

  • Loads RegisterForm defined in app/forms.py

  • GET request

    • loads the page app/tempates/accounts/register.html

  • POST request

    • Validate the input data: username, password, email

    • Check if username or email is already registered

    • Hash the password provided by the user

    • Save the user in the database

    • Reload the registration page and inform the user

Route /logout.html

The route delete the user session and redirect the user to the login.html

Default Route /

The route will serve all pages defined in the app/templates for the authenticated users using a simple flow:

  • Check user is authenticated

  • Redirect to /login.html guests users

  • Load the requested page from app/templates folder

  • Return Error 404 and associated page if requested page cannot be located

  • Return Error 500 if a critical error occurred during the page load

Pages & Assets

Pages served by the starter are organized using a simple folder structure:

< PROJECT ROOT >
|
|-- app/
| |
| |-- static/assets/
| | |-- <css, JS, images> # CSS files, Javascripts files
| |
| |-- templates/
| | |
| | |-- includes/ # Page chunks, components
| | | |
| | | |-- navigation.html # Top bar
| | | |-- sidebar.html # Left sidebar
| | | |-- scripts.html # JS scripts common to all pages
| | | |-- footer.html # The common footer
| | |
| | |-- layouts/ # App Layouts (the master pages)
| | | |
| | | |-- base.html # Used by common pages like index, UI
| | | |-- base-fullscreen.html # Used by auth pages (login, register)
| | |
| | |-- accounts/ # Auth Pages (login, register)
| | | |
| | | |-- login.html # Use layout `base-fullscreen.html`
| | | |-- register.html # Use layout `base-fullscreen.html`
| | |
| | index.html # The default page
| | page-404.html # Error 404 page (page not found)
| | page-500.html # Error 500 page (server error)
| | *.html # All other pages provided by the UI Kit
|
|
|-- ************************************************************************

Static Assets

The folder contains the assets provided by the UI Kit integrated into the app. AppSeed conversion scripts will modify the original UI kit path to match the structure:

  • static/assets - the root directory for all files (JS, images)

  • static/assets/css - CSS files that style the app

  • static/assets/img - Images and Icons

  • static/assets/js - javascript files provided by the UI Kit

  • static/assets/scss - SCSS files, if provided by the UI Kit vendor

Templates Folder

All pages served by the application are located inside this folder.

  • templates/layouts - the directory with app layouts

  • templates/includes - the directory with HTML chunks and components

  • templates/accounts - store the authentication pages (login, registration)

  • templates/ - all pages defined/served by the app are saved at the root of the templates folder

Common pages

This section lists the common pages defined in all Flask applications prototyped on top of this generic starter.

  • login.html, rendered with layouts/base-fullscreen.html

  • register.html, rendered with layouts/base-fullscreen.html

  • index.html, rendered with layouts/base.html

  • page-404.html, rendered with layouts/base.html

  • page-403.html, rendered with layouts/base.html

Data Structures

The Flask starter exposes a short-list with data structures used globally across the app:

current_user object

Constructed by Flask-Login can be used to detect if the current request is executed by an authenticated user or not. The object has global visibility and can be used in all app controllers and handlers but also in views.

How it works

app/views.py define a callback function required by Flask-Login library:

# File: app/views.py
# *****************************************
# `lm` is constructed in `app/__init__.py`
#
# lm = LoginManager( ) # flask-loginmanager
# lm.init_app(app) # init the login manager
#
# *****************************************
#
# provide login manager with load_user callback
@lm.user_loader
def load_user(user_id):
return User.query.get(int(user_id))

Usage in contoler

# File: app/views.py
def index(path):
# Redirect guests users to login page
if not current_user.is_authenticated:
return redirect(url_for('login'))

Usage in view

<div class="collapse navbar-collapse" id="navigation">
<ul class="navbar-nav ml-auto">
<!-- The Usage of <current_user> object -->
{% if current_user.is_authenticated %}
<!-- Html chunk rendered for authenticated users-->
<li class="nav-item">
<a href="/" class="nav-link text-primary">
<i class="tim-icons icon-minimal-left"></i> Back to Dashboard
</a>
</li>
{% else %}
<!-- Html chunk rendered for guests users-->
<li class="nav-item ">
<a href="{{ url_for('register') }}" class="nav-link">
<i class="tim-icons icon-laptop"></i> Register
</a>
</li>
<li class="nav-item ">
<a href="{{ url_for('login') }}" class="nav-link">
<i class="tim-icons icon-single-02"></i> Login
</a>
</li>
{% endif %}
</ul>
</div>