The Sessions Framework
Reviewed & published by Brayan K
A session is server-side storage that remembers data about one visitor across many requests, keyed by an opaque identifier the browser keeps in a cookie.
Part of the free Django course at LearnCodingFast — hands-on lessons with worked examples and the output they print, plus practice exercises and a quick quiz.
In this lesson you'll use request.session like a dictionary — setting, reading, and popping values — control how long sessions live with SESSION_COOKIE_AGE, learn where Django stores them, and flush a session cleanly on logout.
What You'll Learn in This Lesson
- Use request.session as a dictionary
- Set, get (with defaults), and pop session values
- Build a visit counter and flash messages
- Control lifetime with SESSION_COOKIE_AGE
- Understand db, cache, and signed-cookie backends
- Flush a session on logout to clear its data
1 request.session Is a Dictionary
The sessions framework is enabled by default — SessionMiddleware is in MIDDLEWARE and django.contrib.sessions is in INSTALLED_APPS. Inside any view you get request.session, which behaves like a Python dictionary. You set keys, read them with an optional default, and Django saves the changes automatically.
# store/views.py
def profile(request):
# Set values on the session (saved automatically)
request.session["username"] = "ada"
request.session["cart"] = ["book", "pen"]
# Read values, with a safe default when the key is missing
name = request.session.get("username")
theme = request.session.get("theme", "light") # default used
return render(request, "store/profile.html", {
"name": name,
"theme": theme,
})# request.session behaves like a dict that survives between requests.
# Here is a tiny in-memory session store that mirrors the same API:
# set a key, read it back (with a default), and check membership.
class Session(dict):
def get(self, key, default=None):
return super().get(key, default)
# Django gives you request.session; we'll make our own
session = Session()
# Set values (request.session["cart"] = [...])
session["username"] = "ada"
session["cart"] = ["book", "pen"]
# Read values, with a safe default when the key is missing
print("username:", session.get("username"))
print("cart:", session.get("cart"))
print("theme:", session.get("theme", "light")) # default used
print("has cart:", "cart" in session)
# EXPECTED:
# username: ada
# cart: ['book', 'pen']
# theme: light
# has cart: True
# ✅ Expected output:
# username: ada
# cart: ['book', 'pen']
# theme: light
# has cart: True2 A Visit Counter and One-Time Flash Data
The classic session example is a counter that survives reloads. You read the current value with get("visits", 0), add one, and write it back. For data that should appear exactly once — like a "Saved!" notice — use pop(), which reads the value and removes it in a single step.
# store/views.py
def dashboard(request):
# Count visits across requests
request.session["visits"] = request.session.get("visits", 0) + 1
# Read AND remove a one-time message (returns None if absent)
flash = request.session.pop("flash", None)
return render(request, "store/dashboard.html", {
"visits": request.session["visits"],
"flash": flash,
})# Visit counter: a classic session use-case. session.get(key, 0) reads the
# current value (or 0 the first time), then we write the incremented value
# back. session.pop removes a key and returns it.
class Session(dict):
def get(self, key, default=None):
return super().get(key, default)
def pop(self, key, default=None):
return super().pop(key, default)
session = Session()
# Simulate three page views in a row
for _ in range(3):
session["visits"] = session.get("visits", 0) + 1
print("visit count:", session["visits"])
# pop() reads AND removes — handy for one-time "flash" data
session["flash"] = "Saved!"
message = session.pop("flash", None)
print("flash message:", message)
print("flash still there?", "flash" in session)
# EXPECTED:
# visit count: 3
# flash message: Saved!
# flash still there? False
# ✅ Expected output:
# visit count: 3
# flash message: Saved!
# flash still there? False3 Expiry, Storage Backends, and Flushing
A session does not last forever. SESSION_COOKIE_AGE sets the lifetime in seconds (default two weeks). You choose where the data lives with SESSION_ENGINE: the database (default), the cache for speed, files, or a signed cookie that stores the data in the browser itself.
# settings.py
# How long a session lives, in seconds (default = 1209600 = 2 weeks)
SESSION_COOKIE_AGE = 60 * 60 * 24 * 7 # one week
# End the session when the browser is closed
SESSION_EXPIRE_AT_BROWSER_CLOSE = False
# Where session data is stored. Pick ONE:
SESSION_ENGINE = "django.contrib.sessions.backends.db" # default
# SESSION_ENGINE = "django.contrib.sessions.backends.cache" # fast, in cache
# SESSION_ENGINE = "django.contrib.sessions.backends.cached_db" # cache + db
# SESSION_ENGINE = "django.contrib.sessions.backends.signed_cookies" # in browserOn logout you should throw the session data away. request.session.flush() deletes the stored data and rotates the key so the old one is useless — Django's built-in logout() view does this for you.
# store/views.py
from django.contrib.auth import logout
def sign_out(request):
logout(request) # internally calls request.session.flush()
return redirect("home")# Sessions expire. SESSION_COOKIE_AGE is the lifetime in seconds (default
# 1209600 = two weeks). A session is valid only if "now" is before the
# expiry time. flush() throws everything away — what logout() does.
SESSION_COOKIE_AGE = 1209600 # two weeks, Django's default
def expiry_time(created_at, max_age=SESSION_COOKIE_AGE):
return created_at + max_age
def is_valid(created_at, now, max_age=SESSION_COOKIE_AGE):
return now < expiry_time(created_at, max_age)
created = 1000 # pretend the session was created at t=1000
print("expires at:", expiry_time(created))
print("valid at t=1500:", is_valid(created, now=1500))
print("valid at t=2000000:", is_valid(created, now=2000000))
# flush() on logout: drop every key
session = {"username": "ada", "cart": ["book"]}
session.clear() # this is what request.session.flush() does
print("after flush:", session)
# EXPECTED:
# expires at: 1210600
# valid at t=1500: True
# valid at t=2000000: False
# after flush: {}
# ✅ Expected output:
# expires at: 1210600
# valid at t=1500: True
# valid at t=2000000: False
# after flush: {}🎯 Your Turn: Start the Counter at Zero
A visit counter must start at zero the first time, before any value exists in the session. Fill in the blank with the default that makes the count read 1, 2, 3.
# 🎯 YOUR TURN: increment a visit counter stored in the session.
# Replace ___ so the count starts at 0 the first time and goes up by 1.
session = {}
def record_visit(session):
session["visits"] = session.get("visits", ___) + 1
return session["visits"]
print(record_visit(session))
print(record_visit(session))
print(record_visit(session))
# EXPECTED OUTPUT:
# 1
# 2
# 3Common Errors (And How to Fix Them)
❌ KeyError reading a session value
You used request.session["cart"] on a visitor who never set it.
✅ Fix: read with a default — request.session.get("cart", []).
❌ My in-place change to a list/dict isn't saved
request.session["cart"].append(x) mutates the list but Django doesn't notice.
✅ Fix: set request.session.modified = True, or reassign the whole value back.
❌ no such table: django_session
The database session backend needs its table, which you haven't created yet.
✅ Fix: run python manage.py migrate so django_session exists.
📋 Quick Reference
| Call | What It Does |
|---|---|
| request.session["k"] = v | Store a value (saved automatically) |
| request.session.get("k", d) | Read a value with a fallback default |
| request.session.pop("k", d) | Read and remove a value in one step |
| request.session.set_expiry(secs) | Give this session a custom lifetime |
| request.session.flush() | Delete the data and rotate the key (logout) |
🏆 Mini Challenge: A Session-Backed Cart
Build a shopping cart that lives in the session: add items, count them, and clear the cart on checkout — exactly the pattern you'd use with request.session.
# 🏆 Mini Challenge: a shopping cart living in the session.
# add_to_cart appends an item; the cart starts empty if missing.
# cart_total counts items; checkout flushes the cart on success.
def add_to_cart(session, item):
cart = session.get("cart", [])
cart.append(item)
session["cart"] = cart
def cart_total(session):
return len(session.get("cart", []))
def checkout(session):
count = cart_total(session)
session.pop("cart", None) # clear cart after purchase
return f"Ordered {count} item(s). Cart is now empty."
session = {}
add_to_cart(session, "book")
add_to_cart(session, "pen")
add_to_cart(session, "lamp")
print("items in cart:", cart_total(session))
print(checkout(session))
print("items after checkout:", cart_total(session))
# EXPECTED:
# items in cart: 3
# Ordered 3 item(s). Cart is now empty.
# items after checkout: 0❓ Frequently Asked Questions
Lesson complete — your app remembers its visitors!
You can use request.session like a dictionary, set and pop values, build a visit counter and flash messages, control expiry with SESSION_COOKIE_AGE, choose a storage backend, and flush a session cleanly on logout.
Practice quiz
What is a Django session?
- Server-side storage that remembers data across requests
- A single HTTP request
- A database migration
- A URL route
Answer: Server-side storage that remembers data across requests. A session is server-side storage keyed by an identifier the browser keeps in a cookie.
How do you interact with the session inside a view?
- request.cookies
- request.session
- request.store
- request.state
Answer: request.session. request.session behaves like a dictionary you set and read in a view.
Which is the safe way to read a session value that might not exist?
- request.session['key']
- request.session.fetch('key')
- request.session.get('key', default)
- request.session.read('key')
Answer: request.session.get('key', default). request.session.get('key', default) avoids a KeyError when the key is missing.
Which method reads a value and removes it in one step?
- get()
- set()
- keys()
- pop()
Answer: pop(). request.session.pop('flash', None) reads and removes a key, ideal for one-time data.
Which setting controls how long a session lasts, in seconds?
- SESSION_TIMEOUT
- SESSION_COOKIE_AGE
- SESSION_LIFETIME
- COOKIE_MAX_AGE
Answer: SESSION_COOKIE_AGE. SESSION_COOKIE_AGE sets the session lifetime in seconds (default 1209600, two weeks).
What is the default session storage backend?
- The database (django_session table)
- The file system
- A signed cookie
- Redis
Answer: The database (django_session table). By default Django uses the database backend, storing each session as a row in django_session.
Which method deletes session data and rotates the key on logout?
- request.session.clear()
- request.session.delete()
- request.session.flush()
- request.session.reset()
Answer: request.session.flush(). flush() deletes the data and rotates the key; Django's logout() view calls it.
When you mutate a list stored in the session in place, what must you do so it saves?
- Nothing, it always saves
- Restart the server
- Run migrate
- Set request.session.modified = True
Answer: Set request.session.modified = True. Django cannot detect in-place mutations; set session.modified = True or reassign the value.
What does the browser actually hold with the default session backend?
- An opaque session key in a cookie
- The full session data
- The user's password
- Nothing at all
Answer: An opaque session key in a cookie. The browser only holds an opaque session key; the real data stays on the server.
Which setting, when True, ends the session when the browser closes?
- SESSION_COOKIE_AGE
- SESSION_EXPIRE_AT_BROWSER_CLOSE
- SESSION_SAVE_EVERY_REQUEST
- SESSION_COOKIE_SECURE
Answer: SESSION_EXPIRE_AT_BROWSER_CLOSE. SESSION_EXPIRE_AT_BROWSER_CLOSE=True makes the session end when the browser is closed.
Continue this course
- Previous: Pagination with Paginator
- Next: File & Image Uploads — Handle uploads with FileField, ImageField, MEDIA, and request.FILES
- Quick reference: Django cheat sheet
Frequently asked questions
Where does Django actually store session data?
By default Django uses the database backend (django.contrib.sessions.backends.db), which keeps each session as a row in the django_session table. The browser only ever holds a session key in a cookie; the real data stays on the server. You can switch the backend with the SESSION_ENGINE setting to use the cache, the cache backed by the database, files, or a signed cookie that stores the data in the browser itself.
What is the difference between a session and a cookie?
A cookie is a small piece of text the browser stores and sends back with every request. A session is server-side storage keyed by a value held in a cookie. With the default backend the browser only carries an opaque session key in its sessionid cookie, while the actual data — cart contents, flash messages, a visit count — lives safely on the server where the user cannot tamper with it.
How long does a session last?
The lifetime is controlled by SESSION_COOKIE_AGE, which defaults to 1209600 seconds (two weeks). After that the cookie expires and the session is gone. If you set SESSION_EXPIRE_AT_BROWSER_CLOSE to True the session instead ends when the user closes the browser. You can also call request.session.set_expiry() to give a specific session a custom lifetime in seconds.
How do I clear a session when a user logs out?
Call request.session.flush(). It deletes the current session data from the store and rotates the session key, so the old key can no longer be used. Django's built-in logout() view already calls flush() for you. If you only want to remove the data but keep the same key, use request.session.clear() instead.