When building applications that handle user accounts, a lot of functionality depends on storing session variables for users. Consider a typical checkout cart: it's quite often that an abandoned cart on any e-commerce website will retain its contents long after a user abandons it. Carts sometimes even have their contents persist across devices! To build such functionality, we cannot rely on Flask's default method of storing session variables, which happens via locally stored browser cookies. Instead, we can use a cloud key/value store such as Redis, and leverage a plugin called Flask-Session.
Flask-Session is a Flask plugin that enables the simple integration of a server-side cache leveraging methods such as Redis, Memcached, MongoDB, relational databases, and so forth. Of these choices, Redis is an exceptionally appealing option.
Redis is a NoSQL datastore written in C intended to temporarily hold data in memory for users as they blaze mindlessly through your site. Redis was designed for this very purpose, is extremely quick, and is free to use when spinning up a free instance on Redis Labs.
Becoming Familiar with Flask-Session
To understand Flask-Session's offering, a good place to start is by peaking at the settings that Flask-Session accepts:
Specifies which type of session interface to use. Built-in session types:
|SESSION_PERMANENT||Whether to use a permanent session (default: True)|
|SESSION_USE_SIGNER||Whether to sign the session cookie sid or not, if set to True, you have to set flask.Flask.secret_key. (default: False)|
|SESSION_KEY_PREFIX||A prefix that is added before all session keys.
This makes it possible to use the same backend
storage server for different apps. (default:
|SESSION_REDIS||An open Redis session to store session data in.|
|SESSION_MEMCACHED||A memcache.Client instance, default connect to 127.0.0.1:11211|
|SESSION_FILE_DIR||The directory where session files are stored. Default to use flask_session directory under current working directory.|
|The maximum number of items the session stores before it starts deleting some, default 500|
|SESSION_FILE_MODE||The file mode wanted for the session files, default 0600|
|SESSION_MONGODB||A pymongo.MongoClient instance, default connect to 127.0.0.1:27017|
|SESSION_MONGODB_DB||The MongoDB database you want to use, default “flask_session”|
|SESSION_MONGODB_COLLECT||The MongoDB collection you want to use, default “sessions”|
|SESSION_SQLALCHEMY||A flask.ext.sqlalchemy.SQLAlchemy instance whose database connection URI is configured using the SQLALCHEMY_DATABASE_URI parameter|
|SESSION_SQLALCHEMY_TABLE||The name of the SQL table you want to use, default “sessions”|
Even at first glance, it's pretty easy to understand what our options are.
To get started, we need to install 2 libraries:
Redis (along with Flask, obviously):
Next, we need to configure our app. In our config.py file, we need to import the Redis library with
import redis (we'll get to that in a minute). Next, we need to set the following variables in config.py:
SECRET_KEY: Flask-Session won't work without a secret key; it's important to set this to a random string of characters (as always, make sure this is secure).
SESSION_TYPE: Will be set to
SESSION_TYPE=redisfor our purposes.
SESSION_REDIS: The URI of our cloud-hosted Redis instance. Redis URIs are structured a bit uniquely:
The full configuration of a Redis instance using a URI looks like this:
If you're having trouble with your config, feel free to borrow mine (this pulls values from .env):
Initializing a Flask-Session Application
We know much about the Flask application factory and how to initialize other Flask plugins such as Flask-SQLAlchemy and Flask-Login. Flask-Session is initialized in the same way: we set a global variable first and then initialize the plugin with
session.init_app(app). This is an example of an __init__.py file initializing Flask-Session, Flask-SQLAlchemy, and Flask-Login (this builds on the source code we used to implement Flask-Login):
Flask-Session initialized as
session, we're ready to see how this works!
Let's Mess With Some Variables
The cool thing about Flask-Session is that it extends Flask native
session object. Once we've configured our Flask app to use Flask-Session (as we already have), we can work with Flask session variables in the same way we would have if we were still using cookies. To get started, we import
from flask import session.
Let's review the basics of managing values in a Flask session.
Setting a Value
Setting a variable on a session looks a lot like setting values for any old Python dictionary object:
Retrieving a Value
With a value saved to our session, we can retrieve and reuse it with
Removing a Value
If the value we've saved has been used and is no longer needed, we can remove variables from our session using
To see sessions working in action, we'll create a couple of routes that create and display values saved to a session. First, we'll set a session variable in the route for our application's homepage. Next, we'll create a route for the specific purpose of displaying this variable:
At first glance, it doesn't seem like anything has happened on our app's home page:
Behind the scenes, we've set a session variable named
redis_test. Navigate to https://127.0.0.1:5000/session and you'll be greeted with the following:
Not only did we create a value that can persist across views, but since we've stored this value in a cloud Redis instance, it should persist across devices as well. I connected to my Redis instance using a GUI, and this is what came back:
Ahh, the keys and the values we stored are encrypted! This is why it was so important to set our secret key earlier. Nevertheless, we can see our session variables are successfully decoupled. Instead of depending on the user's browser or our app's local server, user session variables are nice and comfy in the cloud.
Get Your Hands Dirty
The best way of learning is by doing, of course. For anybody interested, I've uploaded the source code for this tutorial to Github here:
This has been another episode of Building Flask Apps! Join us next time when we... well, I'm not sure yet. Join us anyway! Peace fam.