Ep. 26 Flask before and after request

10 Apr 2019

Flask before and after request functions | Learning Flask Ep. 26

In this short article, we're going to be taking a look at some of the ways we can run functions before and after a request in Flask, using the before_request and after_request decorators and a few others.

We'll start out with a very basic Flask application:

from flask import Flask

app = Flask(__name__)

@app.route("/")
def index():
    print("index is running!")
    return "Hello world"

if __name__ == "__main__":
    app.run()

before_request

The before_request decorator allows us to create a function that will run before each request.

We can use it by decorating a function with @app.before_request:

@app.before_request
def before_request_func():
    print("before_request is running!")

Adding this function to our application and making a request to the route /, we get the following output in the terminal:

before_request is running!
index is running!

before_request functions are ideal for tasks such as:

  • Opening database connections

  • Loading a user from the session

  • Working with the flask g object

Functions decorated with before_request are not required to return anything, however - If a before_request function returns a non None value, it will be handled as if it was the return value for the view and any further request handling is stopped.

For example:

@app.before_request
def before_request_func():
    print("before_request is running!")
    return "Intercepted by before_request"

If you were to make any requests or go to any route in your application, you'd see Intercepted by before_request as the return value.

Let's import session and g and work with them in the before_request function:

from flask import session, g

To work with the session object, we'll need to assign a secret_key to our app:

app.secret_key = "MySecretKey1234"

For the sake of this example, we'll just assign a key & value to the session object and set an attribute username on the g object.

You would typically use a function like this to:

  • Get a unique user ID from the session

  • Fetch the user from a database

  • Assign the user to the g object

Here's the example:

@app.before_request
def before_request_func():
    session["foo"] = "bar"
    g.username = "root"
    print("before_request is running!")

Running the app and accessing our route gives us the same output as before, so let's access these values from our route:

@app.route("/")
def index():
    username = g.username
    foo = session.get("foo")
    print("index is running!", username, foo)
    return "Hello world"

Running the app and accessing our route, we see the following output in the terminal:

before_request is running!
index is running! root bar

We're able to access session["foo"] and g.username which we set in before_request as it's available in the context of the request.

before_first_request

Functions decorated with @app.before_first_request will run once before the first request to this instance of the application:

@app.before_first_request
def before_first_request_func():
    print("This function will run once")

Running the app and making a couple of requests, we see the following output:

This function will run once
before_request is running!
index is running! root bar
127.0.0.1 - - [10/Apr/2019 13:42:10] "GET / HTTP/1.1" 200 -
before_request is running!
index is running! root bar
127.0.0.1 - - [10/Apr/2019 13:42:12] "GET / HTTP/1.1" 200 -

As you'll notice, the before_first_request fucntion only ran once before the very first request to the app and is ignored on subsequent requests.

Depending on your application, you may want to use a before_first_request function to do some database maintenance or any other task that only needs to happen once.

after_request

Functions decorated with after_request work in the same way as before_request, except they are run after each request.

An important thing to note is that any after_request functions must take and return an instance of the Flask response class.

Here's a simple example:

@app.after_request
def after_request_func(response):
    print("after_request is running!")
    return response

Running our app and making a request to our route, we see:

before_request is running!
index is running! root bar
after_request is running!

after_request functions will be run after every request and provide access to the request context, meaning we still have access to ssession and g. For example:

@app.after_request
def after_request_func(response):
    username = g.username
    foo = session.get("foo")
    print("after_request is running!", username, foo)
    return response

At this point you might be thinking; This is a great place to do something like close a database connection, and you'd be right.

However something to note is that any functions decorated with after_request will NOT run if your application throws an exception.

We can illustrate this by raising an exception in our route:

@app.route("/")
def index():

    raise ValueError("after_request will not run")

    username = g.username
    foo = session.get("foo")
    print("index is running!", username, foo)
    return "Hello world"

Running our app and accessing our route, we see:

This function will run once
before_request is running!
127.0.0.1 - - [10/Apr/2019 14:02:17] "GET / HTTP/1.1" 500 -
Traceback (most recent call last):
# etc ...

As expected, both our before_first_request and before_request functions ran, however raising the ValueError in our route brought our application to a halt and after_request didn't run.

Fortunately, we can get around this with teardown_request.

teardown_request

Functions decorated with teardown_request behave similarly to after_request functions, however, they have the added benefit of being triggered regardless of any exceptions raised.

This makes teardown_request functions a great place to do any cleanup operations after requests, as we know these function will always run.

Note - In debug mode, Flask will not tear down a request on exception immidiately.

From the Flask docs:

"Generally teardown functions must take every necessary step to avoid that they will fail. If they do execute code that might fail they will have to surround the execution of these code by try/except statements and log occurring errors."

When a teardown function was called because of an exception it will be passed an error object.

@app.teardown_request
def teardown_request_func(error=None):

    print("teardown_request is running!")
    if error:
        # Log the error
        print(str(error))

Due to the nature of how Flask handles teardown_request in debug mode, we'll run export FLASK_ENV=production to see our function in action.

Running our app without raising the ValueError in our route, we see:

This function will run once
before_request is running!
index is running! root bar
after_request is running! root bar
teardown_request is running!

We can see that teardown_request ran as expected as it will run regardless of whether an exception was raised or not.

Running our app and raising the ValueError in our route, we see:

This function will run once
before_request is running!
[2019-04-10 15:08:59,003] ERROR in app: Exception on / [GET]
Traceback (most recent call last):
...
teardown_request is running!
after_request will not run

Digging through the debug message in the console, we see that teardown_request has run and the message raised in the exception has been passed to it and printed, whereas after_request was not triggered.

All together

Putting everything together, our application looks like this:

from flask import Flask
from flask import session, g

app = Flask(__name__)
app.secret_key = "iufh4857o3yfhh3"


@app.before_first_request
def before_first_request_func():

    """ 
    This function will run once before the first request to this instance of the application.
    You may want to use this function to create any databases/tables required for your app.
    """

    print("This function will run once ")


@app.before_request
def before_request_func():

    """ 
    This function will run before every request. Let's add something to the session & g.
    It's a good place for things like establishing database connections, retrieving
    user information from the session, assigning values to the flask g object etc..
    We have access to the request context.
    """

    session["foo"] = "bar"
    g.username = "root"
    print("before_request is running!")


@app.route("/")
def index():

    """ 
    A simple route that gets a session value added by the before_request function,
    the g.username and returns a string.
    Uncommenting `raise ValueError` will throw an error but the teardown_request
    funtion will still run.
    """

    # raise ValueError("after_request will not run")

    username = g.username
    foo = session.get("foo")
    print("index is running!", username, foo)
    return "Hello world"


@app.after_request
def after_request_func(response):

    """ 
    This function will run after a request, as long as no exceptions occur.
    It must take and return the same parameter - an instance of response_class.
    This is a good place to do some application cleanup.
    """

    username = g.username
    foo = session.get("foo")
    print("after_request is running!", username, foo)
    return response


@app.teardown_request
def teardown_request_func(error=None):

    """ 
    This function will run after a request, regardless if an exception occurs or not.
    It's a good place to do some cleanup, such as closing any database connections.
    If an exception is raised, it will be passed to the function.
    You should so everything in your power to ensure this function does not fail, so
    liberal use of try/except blocks is recommended.
    """

    print("teardown_request is running!")
    if error:
        # Log the error
        print(str(error))


if __name__ == "__main__":
    app.run()

Wrapping up

Using some of Flasks built in decorators allows us to add another layer of functionality and validation to our applications and should be taken advantage of!

We've only provided a few examples here, and you'll find other useful functions for working with requests, both before and after over at the link to the Flask API documentation below:

Last modified · 10 Apr 2019 Reference : https://pythonise.com/series/learning-flask/python-before-after-request#related

Last updated