The goal of this post is to provide clarity on how the Application and Request contexts work in Flask.
This is part one of a two-part series on Flask Contexts:
- Basics: Understanding the Application and Request Contexts in Flask (this article!)
- Advanced: Deep Dive into Flask's Application and Request Contexts
Contents
Objectives
By the end of this post, you should be able to explain:
- How Flask handles the request object and how this differs from other web frameworks
- What the application and request context are
- Which data is stored in both the Application and Request contexts
- How to utilize
current_app
,test_request_context
, andtest_client
with the correct contexts
You should also be able to fix the following error:
RuntimeError: Working outside of application context.
This typically means that you attempted to use functionality that needed
to interface with the current application object in some way. To solve
this, set up an application context with app.app_context().
Contexts in Flask
Unlike Django and other web frameworks, Flask view functions do not accept a request object that contains metadata about the HTTP request.
Django Example:
def users(request):
if request.method == 'POST':
# Save the form data to the database
# Send response
else:
# Get all users from the database
# Send response
With Flask, you import in the request object like so:
from flask import request
@app.route('/users', methods=['GET', 'POST'])
def users():
if request.method == 'POST':
# Save the form data to the database
# Send response
else:
# Get all users from the database
# Send response
In the Flask example, the request object looks, feels, and acts like a global variable, but it's not.
If the request object were a global variable, you wouldn't be able to run a multi-threaded Flask app since global variables are not thread-safe.
Instead, Flask uses contexts to make a number of objects "act" like globals only for the particular context (a thread, process, or coroutine) being used. In Flask, this is called a context-local.
Context locals are similar to but ultimately different than Python's thread-local implementation for storing data that is specific to a thread. Flask's implementation is more generic in order to allow for workers to be threads, processes, or coroutines.
Data Stored in Flask Contexts
When a request is received, Flask provides two contexts:
Context | Description | Available Objects |
---|---|---|
Application | Keeps track of the application-level data (configuration variables, logger, database connection) | current_app , g |
Request | Keeps track of the request-level data (URL, HTTP method, headers, request data, session info) | request , session |
It's worth noting that each of the above objects are often referred to as "proxies". This just means that they are proxies to global flavors of the objects. For more on this, check out the second post in this series.
Flask handles the creation of these contexts when a request is received. They can cause confusion as you don't always have access to a particular object depending on which state your application is in.
Let's look at a few examples.
Application Context Example
Assume you have the following Flask app:
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return 'Welcome!'
if __name__ == '__main__':
app.run()
First, let's look at how to work with the current_app object to access the Application context.
Within the Python shell, if you try to access the current_app.config
object outside of a view function, you should see the following error:
$ python
>>> from flask import current_app
>>> current_app.config
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "werkzeug/local.py", line 347, in __getattr__
return getattr(self._get_current_object(), name)
File "werkzeug/local.py", line 306, in _get_current_object
return self.__local()
File "flask/globals.py", line 52, in _find_app
raise RuntimeError(_app_ctx_err_msg)
RuntimeError: Working outside of application context.
This typically means that you attempted to use functionality that needed
to interface with the current application object in some way. To solve
this, set up an application context with app.app_context(). See the
documentation for more information.
To access objects exposed by the Application and Request contexts outside of a view function, you need to create the appropriate context first:
# without a context manager
$ python
>>> from app import app
>>> from flask import current_app
>>>
>>> app_ctx = app.app_context()
>>> app_ctx.push()
>>>
>>> current_app.config["ENV"]
'production'
>>> app_ctx.pop()
>>>
# with a context manager
$ python
>>> from app import app
>>> from flask import current_app
>>>
>>> with app.app_context():
... current_app.config["ENV"]
...
'production'
>>>
Request Context Example
You can use the test_request_context method to create a request context:
# without a context manager
$ python
>>> from app import app
>>> from flask import request
>>>
>>> request_ctx = app.test_request_context()
>>> request_ctx.push()
>>>
>>> request.method
'GET'
>>>
>>> request.path
'/'
>>>
>>> request_ctx.pop()
>>>
# with a context manager
$ python
>>> from app import app
>>> from flask import request
>>>
>>> with app.test_request_context('/'):
... request.method
... request.path
...
'GET'
'/'
>>>
test_request_context
is typically used during testing when you want to use request data without the overhead of a full request.
Testing Example
The most common time to run into issues with the Application and Request context is when your app is under test:
import pytest
from flask import current_app
from app import app
@pytest.fixture
def client():
with app.test_client() as client:
assert current_app.config["ENV"] == "production" # Error!
yield client
def test_index_page(client):
response = client.get('/')
assert response.status_code == 200
assert b'Welcome!' in response.data
When run, the tests will fail in the fixture:
$ pytest
________________________ ERROR at setup of test_index_page _____________________
@pytest.fixture
def client():
with app.test_client() as client:
> assert current_app.config["ENV"] == "production"
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
def _find_app():
top = _app_ctx_stack.top
if top is None:
> raise RuntimeError(_app_ctx_err_msg)
E RuntimeError: Working outside of application context.
E
E This typically means that you attempted to use functionality that needed
E to interface with the current application object in some way. To solve
E this, set up an application context with app.app_context(). See the
E documentation for more information.
================================= 1 error in 0.13s =================================
To fix, create an application context before accessing current_app
:
import pytest
from flask import current_app
from app import app
@pytest.fixture
def client():
with app.test_client() as client:
with app.app_context(): # New!!
assert current_app.config["ENV"] == "production"
yield client
def test_index_page(client):
response = client.get('/')
assert response.status_code == 200
assert b'Welcome!' in response.data
Summary
To summarize, use the following objects in view functions, CLI commands, and test functions:
Object | Context | Common Error | Solution |
---|---|---|---|
current_app |
Application Context | Working outside of application context | with app.app_context(): |
g |
Application Context | Working outside of application context | with app.test_request_context('/'): |
request |
Request Context | Working outside of request context | with app.test_request_context('/'): |
session |
Request Context | Working outside of request context | with app.test_request_context('/'): |
The following methods should be utilized during testing:
Flask Method | Description |
---|---|
test_client |
Test client for the Flask app |
test_request_context |
Push Request Context for Testing |
Conclusion
This blog post just scratches the surface on the application and request contexts. Be sure to check out the second part in this series to learn more: Deep Dive into Flask's Application and Request Contexts
Check out the following course on how to build, test, and deploy a Flask application as well: