Flask Message Flashing - get_flashed_messages()


Flask Tip

In Flask, the get_flashed_messages() method is used to retrieve all the flash messages (from the session).

πŸ‘‡

<!-- flash messages -->
{% for message in get_flashed_messages() %}
  <p>{{ message }}</p>
{% endfor %}

Flask - Message Flashing


Flask Tip - Message Flashing

Flash messages are used to provide useful information to the user based on their actions with the app. The flash() method is used to create a flash message to be displayed in the next request.

πŸ‘‡

from flask import request, redirect, url_for, render_template, flash

@stocks_blueprint.route('/add_stock', methods=['GET', 'POST'])
def add_stock():
    if request.method == 'POST':
        # ... save the data ...

        flash(f"Added new stock ({stock_data.stock_symbol})!")  # <-- !!
        return redirect(url_for('stocks.list_stocks'))

    return render_template('stocks/add_stock.html')

Flask Redirect


Flask Tip:

In Flask, the redirect() function is used to redirect a user to a different URL.

redirect() can greatly improve the navigation through a site by automatically redirecting users to their expected pages.

πŸ‘‡

@app.route('/add_stock', methods=['GET', 'POST'])
def add_stock():
    if request.method == 'POST':
        # ... save the data ...

        return redirect(url_for('list_stocks'))  # <-- !!

    return render_template('add_stock.html')

Creating Dynamic URLs in Flask with url_for()


Flask Tip:

In Flask, the url_for() function can be passed an argument to specify the variable part of a URL.

πŸ‘‡

# Flask View Function with Variable Routing (Python)
@stocks_blueprint.route('/stocks/<id>')
def stock_details(id):
    stock = Stock.query.filter_by(id=id).first_or_404()
    return render_template('stocks/stock_details.html', stock=stock)


# Jinja Template (HTML)
<ul>
  {% for stock in stocks %}
    <li>
      <a href="{{ url_for('stocks.stock_details', id=stock.id) }}">{{ stock.stock_symbol }}</a>
    </li>
  {% endfor %}
</ul>

Flask URL Building with url_for()


Flask Tip:

In Flask, the url_for() function builds the URL for a specific function.

url_for() is really useful in templates to easily include URLs.

πŸ‘‡

<header class="site-header">
  <a href="{{ url_for('stocks.index') }}">Flask App</a>
  <nav>
    <ul>
      <li><a href="{{ url_for('users.register') }}">Register</a></li>
      <li><a href="{{ url_for('users.login') }}">Login</a></li>
    </ul>
  </nav>
</header>

Server-side Sessions in Flask with Redis


Flask Tip:

Flask-Session works great with a Redis database!

After configuring the interface to Redis, the session object can be used (but data is stored on the server!).

πŸ‘‡

import redis
from flask import Flask, session, render_template_string
from flask_session import Session


# Create the Flask application
app = Flask(__name__)

# Configure Redis for storing the session data on the server-side
app.config['SESSION_TYPE'] = 'redis'
app.config['SESSION_PERMANENT'] = False
app.config['SESSION_USE_SIGNER'] = True
app.config['SESSION_REDIS'] = redis.from_url('redis://localhost:6379')

# Create and initialize the Flask-Session object AFTER `app` has been configured
server_session = Session(app)

@app.route('/get_email')
def get_email():
    return render_template_string("""<h1>Welcome {{ session['email'] }}!</h1>""")

For more, review Server-side Sessions in Flask with Redis.

How to persist sessions after closing the browser in Flask?


By default, the session object in Flask remains in place until the browser is closed.

However, if you want to change the life of the session object, define the PERMANENT_SESSION_LIFETIME configuration variable after creating the Flask app:

import datetime
app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(minutes=1)

When setting the data in the session, specify that the sessions should be permanent (time will be based on PERMANENT_SESSION_LIFETIME):

# Save the form data to the session object
session['email'] = request.form['email_address']
session.permanent = True

For more, check out Sessions in Flask.

How do you "clear" only specific Flask session variables?


In Flask, data stored in the session object can be deleted by popping a specific element from the object.

πŸ‘‡

from flask import session

@app.route('/delete_email')
def delete_email():
    # Clear the email stored in the session object
    session.pop('email', default=None)
    return '<h1>Session deleted!</h1>'

For more, review Sessions in Flask.

Accessing Flask Session Variables in Jinja Templates


In Flask, the session object can be read (in the same manner as a dictionary) to retrieve data unique to the session. It's conveniently available in Jinja templates as well.

πŸ‘‡

from flask import render_template_string


@app.route('/get_email')
def get_email():
    return render_template_string("""
        {% if session['email'] %}
            <h1>Welcome {{ session['email'] }}!</h1>
        {% else %}
            <h1>Welcome! Please enter your email <a href="{{ url_for('set_email') }}">here.</a></h1>
        {% endif %}
    """)

For more, review Sessions in Flask.

Working with Sessions in Flask


In Flask, you can store information specific to a user for the duration of a session using the session object.

Saving data for use throughout a session allows the Flask app to keep data persistent over multiple requests.

πŸ‘‡

from flask import request, session


@app.route('/set_email', methods=['GET', 'POST'])
def set_email():
    if request.method == 'POST':
        # Save the form data to the session object
        session['email'] = request.form['email_address']
        return redirect(url_for('get_email'))

    return """
        <form method="post">
            <label for="email">Enter your email address:</label>
            <input type="email" id="email" name="email_address" required />
            <button type="submit">Submit</button
        </form>
        """

For more, review Sessions in Flask.

Client-side Sessions in Flask


Sessions in Flask can be considered "client-side", as sessions are stored client-side in browser cookies.

Pros:

  1. Validating and creating sessions is fast (no data storage)
  2. Easy to scale (no need to replicate session data across web servers)

Cons:

  1. Sensitive data cannot be stored in session data, as it’s stored on the web browser
  2. Session data is limited by the size of the cookie (usually 4 KB)
  3. Sessions cannot be immediately revoked by the Flask app

For more, review Sessions in Flask.

How are sessions implemented in Flask?


In order to store data across multiple requests, Flask utilizes cryptographically-signed cookies (stored on the web browser) to store the data for a session. This cookie is sent with each request to the Flask app on the server-side where it’s decoded.

Since session data is stored in cookies that are cryptographically signed (not encrypted!), sessions should NOT be used for storing any sensitive information. You should never include passwords or personal information in session data.

For more, review Sessions in Flask.

Context local objects in Flask with Werkzueg


Werkzueg provides a library for local data storage (context locals) in "werkzeug.local". Context locals expand on thread-local data in Python to work with threads, processes, or coroutines. Each context accesses the data in a context-safe manner.

πŸ‘‡

import random
import threading
import time
from werkzeug.local import LocalStack


# Create a global LocalStack object for storing data about each thread
thread_data_stack = LocalStack()


def long_running_function(thread_index: int):
    """Simulates a long-running function by using time.sleep()."""

    thread_data_stack.push({'index': thread_index, 'thread_id': threading.get_native_id()})
    print(f'Starting thread #{thread_index}... {thread_data_stack}')

    time.sleep(random.randrange(1, 11))

    print(f'LocalStack contains: {thread_data_stack.top}')
    print(f'Finished thread #{thread_index}!')
    thread_data_stack.pop()


if __name__ == "__main__":
    threads = []

    # Create and start 3 threads that each run long_running_function()
    for index in range(3):
        thread = threading.Thread(target=long_running_function, args=(index,))
        threads.append(thread)
        thread.start()

    # Wait until each thread terminates before the script exits by 'join'ing each thread
    for thread in threads:
        thread.join()

    print('Done!')

Hashing Passwords in Flask with Werkzeug Utils


Werkzueg (a key component of Flask) provides a library for hashing passwords.

πŸ‘‡

from werkzeug.security import generate_password_hash, check_password_hash


class User(database.Model):

    ...

    def is_password_correct(self, password_plaintext: str):
        return check_password_hash(self.password_hashed, password_plaintext)

    def set_password(self, password_plaintext: str):
        self.password_hashed = generate_password_hash(password_plaintext)

What is Werkzeug?


Werkzeug (a key component of Flask) provides a set of utilities for creating a Python application that can talk to a WSGI server (e.g., Gunicorn).

Werkzeug provides the following functionality:

  1. Request processing
  2. Response handling
  3. URL routing
  4. Middleware
  5. HTTP utilities
  6. Exception handling

Example:

from werkzeug.wrappers import Request, Response


class HelloWorldApp(object):
    """Implements a WSGI application."""
    def __init__(self):
        pass

    def dispatch_request(self, request):
        """Dispatches the request."""
        return Response('Hello World!')

    def wsgi_app(self, environ, start_response):
        """WSGI application that processes requests and returns responses."""
        request = Request(environ)
        response = self.dispatch_request(request)
        return response(environ, start_response)

    def __call__(self, environ, start_response):
        """The WSGI server calls this method as the WSGI application."""
        return self.wsgi_app(environ, start_response)


def create_app():
    """Application factory function"""
    app = HelloWorldApp()
    return app


if __name__ == '__main__':
    # Run the Werkzeug development server to serve the WSGI application (HelloWorldApp)
    from werkzeug.serving import run_simple
    app = create_app()
    run_simple('127.0.0.1', 5000, app, use_debugger=True, use_reloader=True)

Template Inheritance in Jinja and Flask


Template inheritance is an amazing feature in Jinja (and Flask)! It allows a base template to define a structure and then child templates to define the details. It's similar to classes in object-oriented design.

πŸ‘‡

<!-- base.html -->
<body>
  <main>
    <!-- child template -->
    {% block content %}
    {% endblock %}
  </main>
</body>


<!-- index.html -->
{% extends "base.html" %}

{% block content %}
<h1>Welcome to the Flask App!</h1>
{% endblock %}

Flask - pass variables to templates


Flask Tip - Jinja Templates

You can pass variables as arguments to render_template() to use those variables in a template file.

πŸ‘‡

# app.py
@app.route('/about')
def about():
    return render_template('about.html', organization='TestDriven.io')


# about.html
<h2>Course developed by {{ organization }}.</h2>

Jinja Templates in Flask


The Jinja templating engine is one of the key building blocks of a Flask application.

With them, you can:

  1. Use static HTML template files to decouple routes from HTML
  2. Separate HTML structure from content
  3. Use programming constructs -- variables, conditionals, and for loops to control shown content -- in your templates

A template file (containing variables and logic) is rendered into an output file (typically HTML).

πŸ‘‡

from flask import render_template

@app.route('/')
def index():
    return render_template('index.html')

Parse URL Parameters in Flask


The request object in Flask stores any parsed URL parameters in request.args.

For example: http://localhost/users/login?next=%2Fprofile

πŸ‘‡

from urllib.parse import urlparse
from flask import request, current_app, abort

@users_blueprint.route('/login')
def login():

    ...

    # Redirect the user to the specified URL after login
    if 'next' in request.args:
        next_url = request.args.get('next')

        # Only accept relative URLs
        if urlparse(next_url).scheme != '' or urlparse(next_url).netloc != '':
            current_app.logger.info(f'Invalid next path in login request: {next_url}')
            return abort(400)

        current_app.logger.info(f'Redirecting after valid login to: {next_url}')
        return '<p>User logged in!</p>'

Be careful to avoid URLs when parsing user inputs: http://localhost/login?next=http://givemeyourcreditcard.com.

Flask File Uploads


The request object in Flask can be used for handling file uploads via request.files.

πŸ‘‡

import os
from flask import request, current_app
from werkzeug.utils import secure_filename

@journal_blueprint.route('/upload_file', methods=['POST'])
def upload_file():
    if 'file' in request.files:
        file = request.files['file']
        filename = secure_filename(file.filename)
        file.save(os.path.join(current_app.config['UPLOAD_FOLDER'], filename))
        return '<p>File uploaded!</p>'

Flask Request Object - check if the request was made from a secure protocol


The Flask request object can be used to check that a request was made using a secure protocol via request.is_secure:

  • HTTPS - HTTP Secure
  • WSS - WebSockets over SSL/TLS

πŸ‘‡

from flask import request, current_app


@journal_blueprint.route('/journal', methods=['GET'])
def get_journal():
    # Only support secure protocols (HTTPS or WSS)
    if request.is_secure:
        current_app.logger.info(f'Journal request using protocol: {request.scheme}')
        return '<p>Journal Entries</p>'

How to get form data in Flask?


In Flask, the request object contains any form data submitted, which can then be processed in a view function.

πŸ‘‡

from flask import request

@journal_blueprint.route('/<int:index>', methods=['PUT'])
def update_journal_entry(index):
    if request.method == 'PUT':
        # Update the journal entry in the database
        ...
        entry.update(request.form['entry_title'],
                     request.form['entry_text'])

Flask Request Object - Sender's IP Address


In Flask, the request object can be used to log the IP address of the sender using request.remote_addr.

πŸ‘‡

@users_blueprint.route('/login')
def login():
    if request.method == 'POST':
        # Log in the user
        current_app.logger.info(
            f'New login request from from IP address: {request.remote_addr}'
        )
        return '<p>User logged in!</p>'

    return '<h2>Login Form</h2>'

Flask Request Object - HTTP method used


In Flask, the request object provides information about the request being processed in the view functions.

For example, the request object provides the HTTP method used.

πŸ‘‡

from flask import request

@users_blueprint.route('/register', methods=['GET', 'POST'])
def register():
    if request.method == 'POST':
        # Validate request
        return '<p>New user registered!</p>'

    return '<h2>Registration Form</h2>'

Flask Application Factory Function


The application factory function for a Flask application initializes the Flask application.

The biggest benefit of this approach is being able to create different versions of the Flask application using the same interface (the application factory).

πŸ‘‡

# ----------------------------
# Application Factory Function
# ----------------------------

def create_app():
    # Create the Flask application
    app = Flask(__name__)

    initialize_extensions(app)
    register_blueprints(app)
    configure_logging(app)
    register_app_callbacks(app)
    register_error_pages(app)
    register_cli_commands(app)
    return app

Flask Custom Error Pages


Flask allows custom error pages for specific status codes, which can provide a better user experience by allowing users to easily navigate back through your application

πŸ‘‡

@app.errorhandler(404)
def page_not_found(e):
    return render_template('404.html'), 404

Flask Abort


The abort() function in Flask raises an HTTP exception for the given status code. It's helpful for exiting a view function when an error is detected

πŸ‘‡

@journal_api_blueprint.route('/<int:index>', methods=['GET'])
def get_journal_entry(index):

    ...

    # Check that the journal entry is associated with the current user
    if entry.user_id != user.id:
        abort(403)

    return entry

Flask URL Variables


Flask supports URL variables with the route decorator.

Type Description
string (Default) Accepts any text without slashes
int Accepts positive integers
path Similar to string, but also accepts slashes
uuid Accepts UUID strings

πŸ‘‡

@users_blueprint.route('/<int:id>')
def get_user(id):
    return f'<h2>Data for user #{id}</h2>'

Flask Blueprint CLI commands


Flask Tip - Custom CLI Commands

Custom CLI commands in Flask can be added to specific blueprints, not just to the Flask application.

πŸ‘‡

import click
from flask import Blueprint

users_blueprint = Blueprint('users', __name__)

@users_blueprint.cli.command('init_db')
@click.argument('email')
def create(email):
    """Create a new user."""
    ...
    click.echo(f'Added new user (email: {email}!')


# in terminal
$ flask users --help

Commands:
  create  Create a new user.

Flask CLI Commands


Flask Tip - CLI Commands

The flask command is written using Click. Click can be used to create sub-commands for the flask command.

For example, you can create a CLI command for initializing the database:

from click import echo

@app.cli.command('init_db')
def initialize_database():
    """Initialize the SQLite database."""
    database.drop_all()
    database.create_all()
    click.echo('Initialized the SQLite database!')


# in terminal
$ flask init_db

Custom CLI commands are automatically included in the help information:

$ flask --help

Commands:
  init_db  Initialize the SQLite database.
  run      Run a development server.

The Flask-Migrate package is a great example of using custom CLI commands.

Serving Static Files with Flask


Flask Tip - Static Files

Flask automatically creates a static endpoint to serve static files (like HTML templates, CSS stylesheets, JS files, and images).

For example, to serve an image, copy the image into the "static" folder of the Flask project. Create a new route and navigate to http://127.0.0.1:5000/logo.

πŸ‘‡

from flask import current_app

@app.route('/logo')
def flask_logo():
    return current_app.send_static_file('flask-logo.png')

Get a list of all routes defined in a Flask app


Flask Tip - Flask Routes Command

To easily see all the routes defined in a Flask application, use the routes command.

πŸ‘‡

$ flask routes

Endpoint  Methods  Rule
--------  -------  -----------------------
index     GET      /
static    GET      /static/<path:filename>

Adding Automatic Imports to the Flask Shell


Flask Tip:

Additional automatic imports can be added to the Flask shell using shell_context_processor().

πŸ‘‡

# ... After creating the Flask application (`app`) ...
@app.shell_context_processor
def shell_context():
    return {'database': database}


# in terminal
$ flask shell
>>> print(database)

Prototyping with the Flask Shell


Flask Tip - Flask Shell

Prototyping with the Python interpreter is really beneficial with a Flask application too!

Start the Python interpreter with the Flask application loaded to prototype with it.

πŸ‘‡

$ flask shell
>>> print(app.url_map)
>>> print(app.blueprints)

CSRF Protection in Flask with Flask-WTF


Flask tip:

You can use Flask-WTF to implement CSRF protection for your application.

Example:

from flask import Flask, Response, abort, redirect, render_template, request, url_for
from flask_login import (
    LoginManager,
    UserMixin,
    current_user,
    login_required,
    login_user,
    logout_user,
)
from flask_wtf.csrf import CSRFProtect

app = Flask(__name__)
app.config.update(
    DEBUG=True,
    SECRET_KEY="secret_sauce",
)

login_manager = LoginManager()
login_manager.init_app(app)

csrf = CSRFProtect()
csrf.init_app(app)

...

You can read more here: https://testdriven.io/blog/csrf-flask/.

Flask - async and await


Flask tip:

With Flask >= 2.0 you can create asynchronous route handlers using async/await.

Example:

import asyncio


async def async_get_data():
    await asyncio.sleep(1)
    return "Done!"


@app.route("/data")
async def get_data():
    data = await async_get_data()
    return data

Want to learn more? Check out Async in Flask 2.0.

Calculate the execution time of Flask views


Did you know?

You can use a decorator to time the execution of Flask views.

For exampleπŸ‘‡

from functools import wraps
from timeit import default_timer


def timer(f):
    @wraps(f)
    def wrapper(*args, **kwargs):
        start_time = default_timer()
        response = f(*args, **kwargs)
        total_elapsed_time = default_timer() - start_time
        response += f"<h3>Elapsedtime: {total_elapsed_time}</h3>"
        return response

    return wrapper


@app.route("/")
@timer
def hello_world():
    return "Hello World!"

How can I implement a custom error handler in Flask?


Did you know?

You can register exception handlers to a Flask app based on an exception class or response status code.

An exampleπŸ‘‡

from flask import Flask, jsonify, abort

app = Flask(__name__)


class ValidationException(Exception):
    code = 500
    message = "Unknown error"


@app.errorhandler(ValidationException)
def handle_validation_exception(exc):
    return (
        jsonify({"msssage": exc.message, "exception": exc.__class__.__name__}),
        exc.code,
    )


@app.errorhandler(500)
def handle_internal_server_error(exc):
    return jsonify({"msssage": "Oops!", "exception": "Internal server error"}), 500


@app.route("/")
def hello():
    raise ValidationException()

Flask Sentry Example


Flask tip:

Add Sentry to your Flask app to track unhandled exceptions.

An exampleπŸ‘‡

import sentry_sdk
from flask import Flask
from sentry_sdk.integrations.flask import FlaskIntegration

sentry_sdk.init(
    dsn="your-sentry-dsn",
    integrations=[FlaskIntegration()],
    traces_sample_rate=0.3,
    environment="production",
)

app = Flask(__name__)

Managing session data in Flask


Flask tip:

Setting a value to Flask's session is as simple as:

session['key'] = 'value'

https://testdriven.io/blog/flask-sessions/

For example:

from flask import Flask, redirect, request, session, url_for

# Create the Flask application
app = Flask(__name__)

app.secret_key = "BAD SECRET KEY"


@app.route("/set_email", methods=["GET", "POST"])
def set_email():
    if request.method == "POST":
        # Save the form data to the session object
        session["email"] = request.form["email_address"]
        print(session["email"])
        return redirect(url_for("set_email"))
    return """
        <form method="post">
        <label for="email">Enter your email address:</label>
        <input name="email_address" required / >
        <button type="submit">Submit</button
        </form>
        """