How to cache a variable with Flask?
Question:
I am building a web form using Flask and would like the user to be able to enter multiple entries, and give them the opportunity to regret an entry with an undo button, before sending the data to the database. I am trying to use Flask-Caching but have not managed to set it up properly.
I have followed The Flask Mega-Tutorial for setting up Flask (this is my first Flask app).
+---app
| | forms.py
| | routes.py
| | __init__.py
| +---static
| +---templates
I wonder how I need to configure the Flask app to basically be able to do the following things:
cache.add("variable_name", variable_data)
variable_name = cache.get("variable_name")
cache.clear()
in one of the pages (functions with @app.route decorators)?
In app.init.py I have:
from flask import Flask
from config import Config
from flask_caching import Cache
app = Flask(__name__)
app.config.from_object(Config)
cache = Cache(app, config={'CACHE_TYPE': 'simple'})
from app import routes
In routes.py I have:
from flask import current_app
and I use code below when I try to call the cache.
current_app.cache.add("variable_name", variable_data)
What I get when trying to use the form is the following error:
AttributeError: 'Flask' object has no attribute 'cache'
Pretty much all tutorials I’ve found have simply had the app declaration and all the routes in the same module. But how do I access the cache when I have the routes in another module?
Answers:
In order to use the cache
object in routes.py
, you have to import it first (from where you created it, i.e. app/__init__.py
):
from app import cache
Then use it:
cache.add("variable_name", variable_data)
After piecing together a lot of bits from all over the Internet, I finally got this solution for explicitly caching data with Flask-Caching in an app built with an application factory function. The layout and application factory setup follow the Flask tutorial.
(In my specific use case, I’m using the Google Calendar API and wanted to cache the service built to connect and authenticate as well as results from the queries. No need to re-query every time the user interacts with the page.)
An abbreviated view of my flask app:
/.../mysite/
|--- mysite_flask/
| |--- __init__.py (application factory)
| |--- cache.py (for the flask_caching extension)
| |--- site.py (a blueprint, views, and related functions)
__init__.py:
from flask import Flask
def create_app(test_config=None):
# create and configure app as desired
app = Flask(__name__, instance_relative_config=True)
app.config.from_mapping(
...
)
# check whether testing and for instance folder
...
# importing and initializing other blueprints and views
...
# import site blueprint, views, and functionality
from . import site
app.register_blueprint(site.bp)
app.add_url_rule('/', endpoint='index')
# Extensions
# import cache functionality
from .cache import cache
cache.init_app(app)
return app
cache.py
from flask_caching import Cache
cache = Cache(config={'CACHE_TYPE': 'SimpleCache'})
site.py
# Other required imports
...
from .cache import cache
bp = Blueprint('site', __name__)
# other views and functions
...
@bp.route('/book', methods=('GET', 'POST'))
def book():
# Connect to calendar or fail gracefully
service = cache.get("service") # now the cache is available within a view
if service is None: # service hasn't been added to cache or is expired
try: # rebuild the service to connect to calendar
service = connectToCalendar() # uses google.oauth2 and googleapiclient.discovery
cache.set("service", service) # store service in cache
g.error = False
except Exception as e:
flash(e)
g.error = True
return render_template('site/book.html') # jinja2 template that checks for g.error
# remaining logic for the 'book' view
...
# Cache results of queries
if cache.get("week_{}".format(offset)) is None:
# get new results
week = getWeek(service)
cache.set("week_{}".format(offset), week)
else:
week = cache.get("week_{}".format(offset))
return render_template('site/book.html', <...arguments...>)
1. Client Side Caching (built-in)
- All session data stored on client-side as Cookies.
Example storing explicitly variable: username
from flask import Flask, session, request
app = Flask(__name__)
app.config["SECRET_KEY"] = "any random string"
@app.route("/login", methods=["GET", "POST"])
def login():
if request.method == "POST":
session["username"] = request.form["username"]
# to get value use session["username"]
2. Server Side Caching – (Flask-Caching package)
- All data stored on server-side. Only one cookie key per client.
- Recommended approach by the Flask team.
Example storing explicitly variable: username
from flask import Flask, request
from flask_caching import Cache
app = Flask(__name__)
app.config["SECRET_KEY"] = "any random string"
app.config["CACHE_TYPE"] = "SimpleCache" # better not use this type w. gunicorn
cache = Cache(app)
@app.route("/login", methods=["GET", "POST"])
def login():
if request.method == "POST":
cache.set("username", request.form["username"])
# to get value use cache.get("username")
In case of a multithread server with gunicorn I just tested that the best approach is ['CACHE_TYPE'] = 'FileSystemCache'
take a look here.
I am building a web form using Flask and would like the user to be able to enter multiple entries, and give them the opportunity to regret an entry with an undo button, before sending the data to the database. I am trying to use Flask-Caching but have not managed to set it up properly.
I have followed The Flask Mega-Tutorial for setting up Flask (this is my first Flask app).
+---app
| | forms.py
| | routes.py
| | __init__.py
| +---static
| +---templates
I wonder how I need to configure the Flask app to basically be able to do the following things:
cache.add("variable_name", variable_data)
variable_name = cache.get("variable_name")
cache.clear()
in one of the pages (functions with @app.route decorators)?
In app.init.py I have:
from flask import Flask
from config import Config
from flask_caching import Cache
app = Flask(__name__)
app.config.from_object(Config)
cache = Cache(app, config={'CACHE_TYPE': 'simple'})
from app import routes
In routes.py I have:
from flask import current_app
and I use code below when I try to call the cache.
current_app.cache.add("variable_name", variable_data)
What I get when trying to use the form is the following error:
AttributeError: 'Flask' object has no attribute 'cache'
Pretty much all tutorials I’ve found have simply had the app declaration and all the routes in the same module. But how do I access the cache when I have the routes in another module?
In order to use the cache
object in routes.py
, you have to import it first (from where you created it, i.e. app/__init__.py
):
from app import cache
Then use it:
cache.add("variable_name", variable_data)
After piecing together a lot of bits from all over the Internet, I finally got this solution for explicitly caching data with Flask-Caching in an app built with an application factory function. The layout and application factory setup follow the Flask tutorial.
(In my specific use case, I’m using the Google Calendar API and wanted to cache the service built to connect and authenticate as well as results from the queries. No need to re-query every time the user interacts with the page.)
An abbreviated view of my flask app:
/.../mysite/
|--- mysite_flask/
| |--- __init__.py (application factory)
| |--- cache.py (for the flask_caching extension)
| |--- site.py (a blueprint, views, and related functions)
__init__.py:
from flask import Flask
def create_app(test_config=None):
# create and configure app as desired
app = Flask(__name__, instance_relative_config=True)
app.config.from_mapping(
...
)
# check whether testing and for instance folder
...
# importing and initializing other blueprints and views
...
# import site blueprint, views, and functionality
from . import site
app.register_blueprint(site.bp)
app.add_url_rule('/', endpoint='index')
# Extensions
# import cache functionality
from .cache import cache
cache.init_app(app)
return app
cache.py
from flask_caching import Cache
cache = Cache(config={'CACHE_TYPE': 'SimpleCache'})
site.py
# Other required imports
...
from .cache import cache
bp = Blueprint('site', __name__)
# other views and functions
...
@bp.route('/book', methods=('GET', 'POST'))
def book():
# Connect to calendar or fail gracefully
service = cache.get("service") # now the cache is available within a view
if service is None: # service hasn't been added to cache or is expired
try: # rebuild the service to connect to calendar
service = connectToCalendar() # uses google.oauth2 and googleapiclient.discovery
cache.set("service", service) # store service in cache
g.error = False
except Exception as e:
flash(e)
g.error = True
return render_template('site/book.html') # jinja2 template that checks for g.error
# remaining logic for the 'book' view
...
# Cache results of queries
if cache.get("week_{}".format(offset)) is None:
# get new results
week = getWeek(service)
cache.set("week_{}".format(offset), week)
else:
week = cache.get("week_{}".format(offset))
return render_template('site/book.html', <...arguments...>)
1. Client Side Caching (built-in)
- All session data stored on client-side as Cookies.
Example storing explicitly variable: username
from flask import Flask, session, request
app = Flask(__name__)
app.config["SECRET_KEY"] = "any random string"
@app.route("/login", methods=["GET", "POST"])
def login():
if request.method == "POST":
session["username"] = request.form["username"]
# to get value use session["username"]
2. Server Side Caching – (Flask-Caching package)
- All data stored on server-side. Only one cookie key per client.
- Recommended approach by the Flask team.
Example storing explicitly variable: username
from flask import Flask, request
from flask_caching import Cache
app = Flask(__name__)
app.config["SECRET_KEY"] = "any random string"
app.config["CACHE_TYPE"] = "SimpleCache" # better not use this type w. gunicorn
cache = Cache(app)
@app.route("/login", methods=["GET", "POST"])
def login():
if request.method == "POST":
cache.set("username", request.form["username"])
# to get value use cache.get("username")
In case of a multithread server with gunicorn I just tested that the best approach is ['CACHE_TYPE'] = 'FileSystemCache'
take a look here.