Flask - A list of useful “HOW TO’s”
This article presents a shortlist with useful hints and features that might help developers code faster a Flask project.
This article presents a shortlist with useful hints and features that might help developers code faster a Flask project. For newcomers, Flask is a lightweight WSGI web application framework designed to get started quickly and easily, with the ability to scale up to complex applications.
- #1 - How to code a super simple App in Flask
- #2 - How to structure your project
- #3 - How to enable DEBUG
- #4 - How to set up SQLAlchemy
- #5 - How to use the Flask CLI
- #6 - How to migrate a Database
- #7 - How to define and use a form
- #8 - How to implement authentication - Flask-Login
- #9 - How to read POST data
- #10 - How to redirect in Flask
- #11 - Logging in Flask
- #12 - How to return JSON in Flask
- #13 - How to enable CORS
Ok, let's get back to the real content - the first one is pretty basic and explains how to code a minimal Flask app in less than 1 minute (you need to type fast and have Python3 installed).
#1 - Flask, a minimal app
Open a terminal and install Flask using PIP:
$ pip install Flask
Use your preferred editor to create a file called hello.py
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello():
return f'Hello from Flask!'
Once the file is saved, let's start the app:
$ env FLASK_APP=hello.py flask run
* Serving Flask app "hello"
* Running on http://127.0.0.1:5000/
By visiting the app in the browser we should see "Hello from Flask" message.
Flask App Tutorial
#2 - How to structure your project
Being such a lightweight framework, Flask comes with great flexibility regarding the codebase structure of a project. The one suggested in this article has been used in many Flask starters generated by the AppSeed platform but we are pretty sure there are many other good configurations published on Github or private blogs.
Flask Project Structure
< PROJECT ROOT >
|
|-- app/__init__.py
|-- app/
| |-- 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
|
|-- ************************************************************************
The relevant files:
- run.py - the entry point used to start the application
- requirements.txt - a file that specify all dependencies (for now is just Flask)
- app - the application folder where we will add our code
- app/__init__.py - This file is required to let us use the app as a Python Package
- app/static - this folder will contain design assets: JS, css and images.
- templates - the directory with pages, layouts, and components used by Flask to generate some nice pages for us
Regarding this codebase structure, here is a short list with open-source projects built with a similar files structure:
- (Free) Flask Template Pixel UI - simple Flask starter
- (Free) Flask Template Datta Able - open-source Flask dashboard
- (Free) Flask Template Argon - built on top of popular Argon Design
- (Free) Flask Template Volt uses an open-source Bootstrap 5 Design
#3 - How to enable DEBUG
Sometimes during our development, we need to investigate errors in deep by checking the values of the variables, freeze the application execution and inspect the context or visualize a filtered products list in the user shopping cart. We can achieve this in two ways:
- using logs and inject print() statements all over the place
- using a Debugger and gain full control over the app without writing non-functional code in all controllers and helpers.
Flask comes with a Built-in Debugger that allows us to inspect in deep application runtime execution. Flask automatically detects and enable the debugger by scanning the application environment:
$ export FLASK_ENV=development
$ flask run
When running from Python code, the app object accept the debug variable as an argument:
app.run(debug=True)
At this point, app should run in DEBUG more. Another useful option is to use the debugger toolbar plugin.
Adding an external Debugger Toolbar
This plugin provides useful runtime information like HTTP Headers, configuration, rendered templates, and SqlAlchemy queries executed during requests.
To enable these features is pretty easy. First, we need to install the plugin (via PIP):
$ pip install flask-debugtoolbar
The next step is to use the extension over our Flask app:
from flask import Flask
from flask_debugtoolbar import DebugToolbarExtension
app = Flask(__name__)
# the toolbar is only enabled in debug mode:
app.debug = True
# set a 'SECRET_KEY' to enable the Flask session cookies
app.config['SECRET_KEY'] = '<replace with a secret key>'
toolbar = DebugToolbarExtension(app)
If your product uses an app-factory pattern to construct the Flask application, please use this code snippet:
...
# Initialize the Debug Toolbar
toolbar = DebugToolbarExtension()
# Make sure the app has DEBUG enable
app = create_app()
# Inject the toolbar object in the Flask app object
toolbar.init_app(app)
...
For more information related to Debugging in Flask, please access
- Debugging Application Errors - Flask documentation
- Debugger Toolbar - aka Flask-DebugToolbar module
#4 - How to set up Flask-SQLAlchemy
Flask-SQLAlchemy is a wrapper over the popular SQLAlchemy Python toolkit that empowers developers to manage a database using an object-oriented approach.
The setup required by Flask-SqlAlchemy is minimal:
$ pip3 install flask_sqlalchemy
Once the package is installed, we can use it in the source code:
from flask import Flask
# Import SQLAlchemy
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
# Define the database
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db'
# Inject SQLAlchemy magic
db = SQLAlchemy(app)
# Sample model handled by SqlAlchemy
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True, nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)
def __repr__(self):
return '<User %r>' % self.username
To effectively create a User table in the database, we need to use the Flask CLI (see the next section)
- SQLAlchemy - toolkit page
- Flask-SQLAlchemy - Flask wrapper documentation
#5 - How to use the Flask CLI
To use the Flask CLI, the FLASK_APP
environment variable should point to the application bootstrap script. Once we have this variable present in the working environment, we can launch the Flask console and interact with our application in a different way:
$ export FLASK_APP=app # adjust to mach your entry point
$ flask shell
Using the CLI we can create the table defined in the previous section:
$ flask shell
>>> from app import db #
>>> db.create_all() # All tables are created
Using the CLI we can select and update users (or any other defined tables) as well:
$ flask shell
>>> from app import User
>>> User.query.all()
[<User u'admin'>, <User u'guest'>]
For more resources, please access:
- Flask CLI - module documentation
- Define custom commands - a comprehensive section on this topic
#6 - How to migrate a Database
A migration means our database has changed:
- New table added
- Existing table is updated with a new column
- The type of an existing column has changed. For instance we have a column that store 100 characters and we need to extend it to 500.
To safely update the database, we need to "migrate" to the new structure. The Flask plugin that saves us from doing the dirty work is Flask Migrate.
$ pip install Flask-Migrate
To effectively work, Flask-Migrate must be injected in the Flask object:
...
app = Flask(__name__)
...
migrate = Migrate(app, db) # <- The magic line
If we execute the migration for the first time, we should run:
$ # This will create the migrations folder
$ flask db init
Let's update the User model with a new field: role:
# Sample model handled by SqlAlchemy
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True, nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)
# The new field
role = db.Column(db.Integer, primary_key=False)
To update the database, two steps are required: generate the SQL according to our change and apply it on the database.
Generate the SQL
$ flask db migrate
Update the Database - using the SQL generated in the previous step
$ flask db upgrade
At this point, we can inspect the table to visualize the presence of the role column and use it in our code.
Resources
- Flask-Migrate - the official module documentation
- Flask – Setting up Postgres, SQLAlchemy, and Alembic - a nice tutorial on this hot topic
#7 - How to define and use a form
Flask assists us a lot regarding forms via a dedicated library: flask_wtf/FlaskForm. We can easily define, secure, and validate forms using this package.
Here is the definition of a Login form:
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField
class LoginForm(FlaskForm):
username = StringField (u'Username' , validators=[DataRequired()])
password = PasswordField(u'Password' , validators=[DataRequired()])
As mentioned, FlaskForm comes with pre-defined types and validators for password, mandatory fields check, email and much more.
The view (aka the page) that use it:
<form role="form" method="post" action="">
{{ form.username(placeholder="Username") }}
{{ form.password(placeholder="Password",type="password") }}
</form>
To secure the form using a CRSF token we need a single line:
<form role="form" method="post" action="">
{{ form.hidden_tag() }} <!-- CSRF Protection -->
{{ form.username(placeholder="Username") }}
{{ form.password(placeholder="Password",type="password") }}
</form>
For newcomers, a simplified CSRF mechanism is presented below:
- The server generates the CRSF Token
- The form is built and sent to the client browser along with the token
- The user fills and submits the form
- The server receives a new POST request and checks the CSRF token authenticity
- If the token is not validated request is rejected with a 403 (not authorized) status
More forms resources
- Creating Forms in Flask
- CSRF Protection in Flask - how to use it
#8 - How to implement authentication - Flask-Login
Flask-Login provides user session management for Flask. It handles the common tasks of logging in, logging out, and remembering users’ sessions.
Let's highlight the code to integrate Flask-Login in a real application.
Update your user model to inherit the UserMixin class. This is necessary because Flask expects a minimum set of handlers to be implemented by the user model: is_authenticated, is_active, get_id. To skip over this work, UserMixin class saves the day for us.
from app import db
from flask_login import UserMixin
# User model uses UserMixin as parent class
class User(UserMixin, db.Model):
id = db.Column(db.Integer, primary_key=True)
user = db.Column(db.String(64), unique = True)
email = db.Column(db.String(120), unique = True)
password = db.Column(db.String(500))
The next step is to define user_loader a method used by Login-Manager to identify the current authenticated user information: id, email, and all other fields provided by the User class.
...
# provide login manager with load_user callback
@lm.user_loader
def load_user(user_id):
return User.query.get(int(user_id))
...
Once we have the User table updated in the database, we can successfully login.
The login view (aka login page)
...
<form method="post" action="" class="mt-4">
{{ form.hidden_tag() }}
{{ form.username(class="form-control") }}
{{ form.password(class="form-control", type="password") }}
<button type="submit" name="login">Sign in</button>
</form>
Once the user fills in the user and the password, all information is sent to the server via a POST request.
Login - The controller code
# Authenticate user
@app.route('/login.html', methods=['GET', 'POST'])
def login():
# Declare the login form
form = LoginForm(request.form)
# The information returned to the user
msg = "Your cool!"
# Apply all validations defined in the form
if form.validate_on_submit():
# recover form information
username = request.form.get('username', '', type=str)
password = request.form.get('password', '', type=str)
# recover the user from database using username (unique information)
user = User.query.filter_by(user=username).first()
# If the user is found, validate the password
if user:
# BC = BCrypt library
if bc.check_password_hash(user.password, password):
# The user is now authenticated
login_user(user)
else:
msg = "Wrong password. Please try again."
else:
msg = "Unknown user"
return render_template( 'accounts/login.html', form=form, msg=msg )
The controller flow explained:
- Local variable form is initialized using the request form (sent by the user)
- If the form is valid (user and password are present) the code extracts the values
- Select the user from the database using the name
- Check the password by comparing the database value with the one provided by the user and hashed by the BCrypt library.
- If all is good, the user session is created via "login_user()" handler
- Error cases are flagged with a message to inform the user what was wrong.
Flask authentication resources:
- Flask-Login - the concerned module
- LoginManager class definition
- Flask Pixel UI - a simple FREE starter with this flow already coded
#9 - How to read POST data
For newcomers, POST and GET are different methods used to submit information to a server. During a GET request, the variables are appended to the page URL:
GET/login.html?user=Biden&pass=MAGA2020
POST requests use the request body to bundle the information submitted by the user.
In both cases, we can easily read POST and GET parameters with Flask via the request object. Here is a list of request objects that we can refer to extract the information:
- request.args: the key/value pairs in the URL query string
- request.form: the key/value pairs in the body, from an HTML post form
- request.json: parsed JSON data
A real sample - used to handle registration flow
@app.route('/register.html', methods=['GET', 'POST'])
def register():
# declare the Registration Form
form = RegisterForm(request.form)
# request is GET - just display the form
if request.method == 'GET':
return render_template( 'accounts/register.html', form=form, msg=msg )
# Here we have a POST request
if form.validate_on_submit():
# READ information from a POST request
username = request.form.get('username', '', type=str)
password = request.form.get('password', '', type=str)
email = request.form.get('email' , '', type=str)
As mentioned above, the composite request.form
provides the form fields in key/value pairs bundled in the request body.
More information on this topic:
- Flask request Args - object information
- Get the data received in a Flask request - Related StackOverflow topic
#10 - How to redirect in Flask
This is a simple one. Flask exposes a "redirect" handler to keep us lazy:
import os
from flask import Flask,redirect
app = Flask(__name__)
@app.route('/')
def not_me():
# redirect to another page
return redirect("/register")
# redirect to external page
return redirect("https://google.com")
# redirect with HTTP code
return redirect("https://google.com", code=301)
Resources:
- Flask redirect - handler docs
- Flask redirect Example - tutorial provided by Full-Stack Python
#11 - Logging using standard Python library
Flask uses the native Python logging system (no configuration required). To log a message during a request, we need to call the logger interface and set the message we want to be visible in logs:
from flask import Flask
import logging
app = Flask(__name__)
logging.basicConfig(level=logging.DEBUG)
@app.route('/')
def hello():
app.logger.info('Logging is up!')
return 'Hello World!'
if __name__ == '__main__':
app.run()
By running the app, we should see the logging message in the console:
* Serving Flask app "app.py"
* Environment: development
INFO:werkzeug: * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
INFO:flask.app:Logging is up
INFO:werkzeug:127.0.0.1 - - [01/Jun/2019 17:03:09] "GET / HTTP/1.1" 200 -
Logs might be useful when we want to trace runtime errors in production or save other relevant events in the logs for the application owner.
The above sample will push the log to the console but we can easily push messages to a file:
from flask import Flask
import logging
app = Flask(__name__)
logging.basicConfig(filename='app.log', level=logging.DEBUG)
@app.route('/')
def hello():
app.logger.info('Logging is up!')
return 'Hello World!'
if __name__ == '__main__':
app.run()
After a first request, a new file named "app.log" should be created with our log message as content.
Flask logging resources
- Flask Logging - the official module documentation
- Flask Logging - Get starter quickly - a nice tutorial.
#12 - How to return JSON
Flask comes with native support for JSON management via jsonify interface. Let's take a look at this self-descriptive sample:
from flask import jsonify
@app.route('/_get_current_user')
def get_json():
return jsonify(
message="Hello",
from="from JSON"
)
The output should be as below:
{
"message": "Hello",
"from": "from JSON"
}
Something useful: Let's return a registered user using JSON format:
from flask import jsonify
@app.route('/user_json')
def get_json():
user = User.query.filter_by(username='testme').first()
# Using the user object (without __dict__) will generate a runtime error
# TypeError: 'user' is not JSON serializable
return jsonify( user.__dict__ )
The response should contain all fields defined in the User table:
{
"id": "1",
"username": "testme",
"email":"test@google.com"
}
More resources:
- Flask jsonify - class documentation
- Serializing class instance to JSON - related StackOverflow topic
#13 - How to enable CORS in Flask
This feature is useful when our Flask backend is consumed by decoupled entities like a mobile UI interface, external systems that push or request the information provided by the Flask backend.
As usual, Flask has a module for this flask-cors.
$ Install the module via PIP
$ pip install -U flask-cors
Let's take a look at the sample provided in the official docs:
from flask import Flask
from flask_cors import CORS
app = Flask(__name__)
CORS(app)
@app.route("/")
def helloWorld():
return "Hello, cross-origin-world!"
We can also isolate CORS for specific paths like /api/*:
app = Flask(__name__)
cors = CORS(app, resources={r"/api/*": {"origins": "*"}})
@app.route("/api/v1/users")
def list_users():
return "user example"
CORS Resources:
- Flask-CORS - official documentation
- What is CORS - a tutorial provided by Codecademy
Thank you! For more resources, please access
- Flask - the official page
- Flask Tutorial - (Github) Content provided by experienced developers
- Flask Cheat Sheet - how to start fast with Flask