Multiple Apps
The application context (and its purpose) is indeed confusing until you realize that Flask can have multiple apps. Imagine the situation where you want to have a single WSGI Python interpreter run multiple Flask application. We're not talking Blueprints here, we're talking entirely different Flask applications.
You might set this up similar to the Flask documentation section on "Application Dispatching" example:
from werkzeug.wsgi import DispatcherMiddleware
from frontend_app import application as frontend
from backend_app import application as backend
application = DispatcherMiddleware(frontend, {
'/backend': backend
})
Notice that there are two completely different Flask applications being created "frontend" and "backend". In other words, the Flask(...)
application constructor has been called twice, creating two instances of a Flask application.
Contexts
When you are working with Flask, you often end up using global variables to access various functionality. For example, you probably have code that reads...
from flask import request
Then, during a view, you might use request
to access the information of the current request. Obviously, request
is not a normal global variable; in actuality, it is a context local value. In other words, there is some magic behind the scenes that says "when I call request.path
, get the path
attribute from the request
object of the CURRENT request." Two different requests will have a different results for request.path
.
In fact, even if you run Flask with multiple threads, Flask is smart enough to keep the request objects isolated. In doing so, it becomes possible for two threads, each handling a different request, to simultaneously call request.path
and get the correct information for their respective requests.
Putting it Together
So we've already seen that Flask can handle multiple applications in the same interpreter, and also that because of the way that Flask allows you to use "context local" globals there must be some mechanism to determine what the "current" request is (in order to do things such as request.path
).
Putting these ideas together, it should also make sense that Flask must have some way to determine what the "current" application is!
You probably also have code similar to the following:
from flask import url_for
Like our request
example, the url_for
function has logic that is dependent on the current environment. In this case, however, it is clear to see that the logic is heavily dependent on which app is considered the "current" app. In the frontend/backend example shown above, both the "frontend" and "backend" apps could have a "/login" route, and so url_for('/login')
should return something different depending on if the view is handling the request for the frontend or backend app.
To answer your questions...
What is the purpose of the "stack" when it comes to the request or
application context?
From the Request Context docs:
Because the request context is internally maintained as a stack you
can push and pop multiple times. This is very handy to implement
things like internal redirects.
In other words, even though you typically will have 0 or 1 items on these stack of "current" requests or "current" applications, it is possible that you could have more.
The example given is where you would have your request return the results of an "internal redirect". Let's say a user requests A, but you want to return to the user B. In most cases, you issue a redirect to the user, and point the user to resource B, meaning the user will run a second request to fetch B. A slightly different way of handling this would be to do an internal redirect, which means that while processing A, Flask will make a new request to itself for resource B, and use the results of this second request as the results of the user's original request.
Are these two separate stacks, or are they both part of one stack?
They are two separate stacks. However, this is an implementation detail. What's more important is not so much that there is a stack, but the fact that at any time you can get the "current" app or request (top of the stack).
Is the request context pushed onto a stack, or is it a stack itself?
A "request context" is one item of the "request context stack". Similarly with the "app context" and "app context stack".
Am I able to push/pop multiple contexts on top of eachother? If so,
why would I want to do that?
In a Flask application, you typically would not do this. One example of where you might want to is for an internal redirect (described above). Even in that case, however, you would probably end up having Flask handle a new request, and so Flask would do all of the pushing/popping for you.
However, there are some cases where you'd want to manipulate the stack yourself.
Running code outside of a request
One typical problem people have is that they use the Flask-SQLAlchemy extension to set up a SQL database and model definition using code something like what is shown below...
app = Flask(__name__)
db = SQLAlchemy() # Initialize the Flask-SQLAlchemy extension object
db.init_app(app)
Then they use the app
and db
values in a script that should be run from the shell. For example, a "setup_tables.py" script...
from myapp import app, db
# Set up models
db.create_all()
In this case, the Flask-SQLAlchemy extension knows about the app
application, but during create_all()
it will throw an error complaining about there not being an application context. This error is justified; you never told Flask what application it should be dealing with when running the create_all
method.
You might be wondering why you don't end up needing this with app.app_context()
call when you run similar functions in your views. The reason is that Flask already handles the management of the application context for you when it is handling actual web requests. The problem really only comes up outside of these view functions (or other such callbacks), such as when using your models in a one-off script.
The resolution is to push the application context yourself, which can be done by doing...
from myapp import app, db
# Set up models
with app.app_context():
db.create_all()
This will push a new application context (using the application of app
, remember there could be more than one application).
Testing
Another case where you would want to manipulate the stack is for testing. You could create a unit test that handles a request and you check the results:
import unittest
from flask import request
class MyTest(unittest.TestCase):
def test_thing(self):
with app.test_request_context('/?next=http://example.com/') as ctx:
# You can now view attributes on request context stack by using `request`.
# Now the request context stack is empty