Reputation: 3746
my source code has this structure:
main.py
:
from flask import Flask, g
app = Flask(__name__)
with app.app_context():
g.my_db = PostgreSQL()
app.register_blueprint(my_app, url_prefix="/my_app")
my_app.py
:
from flask import Blueprint, g
my_app = Blueprint("my_app", __name__)
@my_app.route("/")
def index():
return g.my_db.fetch_all() <<< ERROR
but it shows this error:
AttributeError: '_AppCtxGlobals' object has no attribute 'my_db'
Even when I try to use g
outside of app context, it shows this error:
RuntimeError: Working outside of application context.
So how to set and access to global variables in Flask?
Upvotes: 9
Views: 7698
Reputation: 151
This question rattled my brain as well and after reading some the responses here I have a better understanding why the g
object doesn't work in the registered blueprint.
It comes down to that each context is unique and does not know about each other even if they are parent/child threads.
According to the following quote, the g
object has the same lifetime as the app context. Thus, within your with app.app_context()
block, g
object persists.
The application context is a good place to store common data during a request or CLI command. Flask provides the g object for this purpose. It is a simple namespace object that has the same lifetime as an application context.
This following quote mentions that g
object isn't idea for storing data between requests, however it doesn't really explain why. Even though you only have one request in your example, this is a good thing to note.
The g name stands for “global”, but that is referring to the data being global within a context. The data on g is lost after the context ends, and it is not an appropriate place to store data between requests. Use the session or a database to store data across requests.
This is the crucial part. The blueprint that you registered is defining a new view function and new route, when invoked, it is a new request. A new request will push a new request context and application context. So now you have a new nested application context.
When a Flask application begins handling a request, it pushes a request context, which also pushes an app context. When the request ends it pops the request context then the application context.
I thought that the nested context would inherit the parent context, but according to this quote below, each context is unique and won't even know about each other even if they are parent/child threads.
The context is unique to each thread (or other worker type). request cannot be passed to another thread, the other thread has a different context space and will not know about the request the parent thread was pointing to.
Upvotes: 0
Reputation: 1705
g
isn't persistent in the way you're trying to use it. Write a function to create a connection each time you need it. Preferably use a database extension like Flask-SQLAlchemy to manage connections for you.
db.py
:
import <postgresql dependencies>
def get_db():
db = PostgreSQL()
# config here
return db
main.py
:
from flask import Flask
app = Flask(__name__)
app.register_blueprint(my_app, url_prefix="/my_app")
my_app.py
:
from flask import Blueprint, g
from db import get_db
my_app = Blueprint("my_app", __name__)
@my_app.route("/")
def index():
db = get_db()
data = db.fetch_all()
db.close()
return data
Upvotes: 3
Reputation: 13347
This happens because the data are lost when the context (with app.app_context()
) ends (doc).
Inside the context, everything is ok :
from flask import Flask, g
app = Flask(__name__)
with app.app_context():
g.my_db = 'database ok'
print(g.my_db)
# >>> this prints 'database ok'
But outside, you cannot access the attribute :
from flask import Flask, g
app = Flask(__name__)
with app.app_context():
g.my_db = 'database ok'
print(g.my_db)
# >>> this throws RuntimeError: Working outside of application context
even if you create a new context:
from flask import Flask, g
app = Flask(__name__)
with app.app_context():
g.my_db = 'database ok'
with app.app_context():
print(g.my_db)
>>> this throws AttributeError: '_AppCtxGlobals' object has no attribute 'my_db'
Your best call should be to declare the database object before the context, and then import it. Or maybe you can create it directly inside my_app.py
where you need it ?
Upvotes: 4