Context Processors
Reviewed & published by Brayan K
A context processor is a function that takes the request and returns a dictionary of variables, which Django merges into every template's context so those variables are available in all templates without each view passing them.
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 see how Django merges context dictionaries, configure the context_processors list in the TEMPLATES setting, write your own custom processor, and understand the built-in request, auth, and messages processors.
What You'll Learn in This Lesson
- What a context processor is and why it exists
- How Django merges context dictionaries per request
- The TEMPLATES context_processors setting
- Writing your own custom context processor
- Built-in processors: request, auth, and messages
- When to use a processor vs passing in the view
1 How Django Merges the Template Context
When you render a template, Django doesn't just use the dictionary your view returns. It builds the final context by merging several dictionaries: one dictionary from each context processor listed in your settings, plus the dictionary your view passes. The processors run first, and then the view's values are layered on top, so a key from your view wins over the same key from a processor.
This is the mental model. In your settings, the processors are listed in TEMPLATES:
# settings.py
TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
"DIRS": [],
"APP_DIRS": True,
"OPTIONS": {
"context_processors": [
"django.template.context_processors.request",
"django.contrib.auth.context_processors.auth",
"django.contrib.messages.context_processors.messages",
],
},
},
]# Django builds the final template context by MERGING dicts:
# 1. one dict from EACH context processor in TEMPLATES
# 2. the dict you return from your view (merged LAST, so it wins)
# This simulation reproduces that merge in plain Python.
def request_processor(request):
# mirrors django.template.context_processors.request
return {"request": request}
def auth_processor(request):
# mirrors django.contrib.auth.context_processors.auth
return {"user": request["user"], "perms": request["user"]["perms"]}
def site_settings(request):
# a CUSTOM processor you write yourself
return {"site_name": "LearnCodingFast", "support_email": "help@example.com"}
CONTEXT_PROCESSORS = [request_processor, auth_processor, site_settings]
def build_context(request, view_context):
final = {}
for processor in CONTEXT_PROCESSORS:
final.update(processor(request)) # processors run first
final.update(view_context) # the view wins on conflicts
return final
request = {"path": "/dashboard/", "user": {"name": "Alice", "perms": ["edit"]}}
view_context = {"title": "Dashboard"}
context = build_context(request, view_context)
for key in sorted(context):
print(f"{key} -> {context[key]}")
# EXPECTED: keys from all three processors PLUS the view's title,
# printed alphabetically (perms, request, site_name, support_email,
# title, user).
# ✅ Expected output:
# perms -> ['edit']
# request -> {'path': '/dashboard/', 'user': {'name': 'Alice', 'perms': ['edit']}}
# site_name -> LearnCodingFast
# support_email -> help@example.com
# title -> Dashboard
# user -> {'name': 'Alice', 'perms': ['edit']}2 Writing Your Own Context Processor
A context processor is just a function that accepts the request and returns a dictionary. By convention you put these functions in a file named context_processors.py inside your app. Suppose you want the current year and a set of navigation links to be available in every template — perfect for a footer and a shared navbar.
# myapp/context_processors.py
import datetime
def common_context(request):
return {
"current_year": datetime.date.today().year,
"nav_links": [
{"label": "Home", "url": "/"},
{"label": "Courses", "url": "/courses/"},
{"label": "Profile", "url": request.path},
],
}Then add its dotted path to the context_processors list so Django runs it on every request:
# settings.py (inside TEMPLATES["OPTIONS"]["context_processors"])
"myapp.context_processors.common_context",Now any template can use the values directly, with no view changes:
<!-- base.html -->
<footer>© {{ current_year }} LearnCodingFast</footer>
<nav>
{% for link in nav_links %}
<a href="{{ link.url }}">{{ link.label }}</a>
{% endfor %}
</nav># A context processor is just a function that takes the request and
# returns a dict. Here we write one that adds the current year and a
# list of nav links to EVERY template's context.
def common_context(request):
return {
"current_year": 2026,
"nav_links": [
{"label": "Home", "url": "/"},
{"label": "Courses", "url": "/courses/"},
{"label": "Profile", "url": request["path"]},
],
}
# Simulate Django calling it during template rendering
request = {"path": "/profile/"}
extra = common_context(request)
print("Footer year:", extra["current_year"])
print("Nav links:")
for link in extra["nav_links"]:
print(f" {link['label']} -> {link['url']}")
# EXPECTED:
# Footer year: 2026
# Nav links:
# Home -> /
# Courses -> /courses/
# Profile -> /profile/
# ✅ Expected output:
# Footer year: 2026
# Nav links:
# Home -> /
# Courses -> /courses/
# Profile -> /profile/3 The Built-in Processors: request, auth, and messages
Most of the variables you take for granted in templates come from Django's built-in processors. The auth processor is the reason every template can reference {{ user }} and {{ perms }} — the view never has to pass them. The request processor exposes {{ request }} so you can read the current path or query parameters. The messages processor exposes flash messages.
<!-- Thanks to the auth processor, these work in ANY template -->
{% if user.is_authenticated %}
<p>Welcome back, {{ user.username }}!</p>
{% endif %}
{% if perms.blog.add_post %}
<a href="/blog/new/">New Post</a>
{% endif %}
<!-- Thanks to the request processor -->
<p>You are on: {{ request.path }}</p>- django.template.context_processors.request — adds request.
- django.contrib.auth.context_processors.auth — adds user and perms.
- django.contrib.messages.context_processors.messages — adds messages.
- django.template.context_processors.static — adds the STATIC_URL.
# Built-in processors expose request, user, and perms to every template.
# This simulates how a template can reference {{ user }} and
# {% if perms.blog.add_post %} without the view passing them.
def auth_context(request):
user = request["user"]
return {
"user": user,
"perms": {p: True for p in user["perms"]},
}
request = {
"path": "/blog/new/",
"user": {"name": "Bob", "is_authenticated": True, "perms": ["blog.add_post"]},
}
ctx = auth_context(request)
# Templates can now do {% if user.is_authenticated %} ...
if ctx["user"]["is_authenticated"]:
print(f"Welcome back, {ctx['user']['name']}!")
# ... and {% if perms.blog.add_post %} ...
if ctx["perms"].get("blog.add_post"):
print("Show the 'New Post' button")
else:
print("Hide the 'New Post' button")
# EXPECTED:
# Welcome back, Bob!
# Show the 'New Post' button
# ✅ Expected output:
# Welcome back, Bob!
# Show the 'New Post' button🎯 Your Turn: Return a Dictionary
A context processor must return a dictionary. Replace the blank so feature_flags makes show_beta available to every template.
# 🎯 YOUR TURN: a context processor MUST return a dict.
# Replace ___ so the function returns a dict with key "show_beta" set to True.
def feature_flags(request):
return ___
result = feature_flags({"path": "/"})
print(result)
# EXPECTED OUTPUT:
# {'show_beta': True}Common Errors (And How to Fix Them)
❌ My processor variable isn't showing up in the template
You wrote the function but never added it to the settings list, so Django never calls it.
✅ Fix: add its dotted path (e.g. "myapp.context_processors.common_context") to context_processors in TEMPLATES["OPTIONS"].
❌ TypeError: context processor didn't return a dictionary
Your function forgot the return, or returned None, so Django can't merge it.
✅ Fix: always end the processor with return {...} — return an empty dict {} if there's nothing to add.
❌ {{ user }} or {{ request }} is blank everywhere
A built-in processor was removed from the list, so its variables are no longer injected.
✅ Fix: make sure the request, auth, and messages processors are still listed in context_processors.
📋 Quick Reference
| Item | What It Does |
|---|---|
| def proc(request): | Signature of a context processor — takes the request |
| return {"key": value} | Must return a dict that is merged into every context |
| context_processors | The list in TEMPLATES["OPTIONS"] where you register it |
| ...auth | Built-in: exposes user and perms |
| ...request | Built-in: exposes the request object |
🎯 Mini Challenge: Register and Merge Two Processors
Build two processors — one for theme/site name, one for a cart count — merge them, and confirm a per-view value overrides a processor default.
# 🎯 Mini Challenge: register multiple processors and merge them,
# then confirm a per-view value overrides a processor default.
def theme_processor(request):
return {"theme": "light", "site_name": "LearnCodingFast"}
def cart_processor(request):
return {"cart_count": len(request["cart"])}
PROCESSORS = [theme_processor, cart_processor]
def render_context(request, view_context):
merged = {}
for proc in PROCESSORS:
merged.update(proc(request))
merged.update(view_context) # view overrides processor defaults
return merged
request = {"path": "/shop/", "cart": ["pen", "ruler", "glue"]}
# The view overrides the processor's default theme:
view_context = {"theme": "dark", "title": "Shop"}
ctx = render_context(request, view_context)
print("theme:", ctx["theme"]) # dark (view wins)
print("site_name:", ctx["site_name"]) # from processor
print("cart_count:", ctx["cart_count"]) # computed by processor
print("title:", ctx["title"]) # from view
# EXPECTED:
# theme: dark
# site_name: LearnCodingFast
# cart_count: 3
# title: Shop❓ Frequently Asked Questions
Lesson complete — your variables reach every template!
You learned how Django merges context dictionaries on each request, where to register processors in the TEMPLATES context_processors list, how to write a custom processor that returns a dict, and what the built-in request, auth, and messages processors give you for free.
Practice quiz
What does a Django context processor return?
- A rendered HTML string
- A dictionary merged into every template context
- An HttpResponse object
- A list of templates
Answer: A dictionary merged into every template context. A context processor takes the request and returns a dict that Django merges into the context of every rendered template.
Where do you register a custom context processor?
- In urls.py
- In the MIDDLEWARE setting
- In the context_processors list inside TEMPLATES['OPTIONS']
- In the INSTALLED_APPS list
Answer: In the context_processors list inside TEMPLATES['OPTIONS']. You add the function's dotted path to the context_processors list in the TEMPLATES setting's OPTIONS.
Which built-in processor exposes {{ user }} and {{ perms }} to templates?
- django.contrib.auth.context_processors.auth
- django.template.context_processors.request
- django.contrib.messages.context_processors.messages
- django.template.context_processors.static
Answer: django.contrib.auth.context_processors.auth. The auth context processor makes user and perms available in every template.
What argument does a context processor function receive?
- The template name
- The response
- The view function
- The request
Answer: The request. A context processor is a function that accepts the current request object.
Which built-in processor exposes {{ request }} in templates?
- django.template.context_processors.request
- django.contrib.auth.context_processors.auth
- django.template.context_processors.csrf
- django.contrib.messages.context_processors.messages
Answer: django.template.context_processors.request. The request context processor adds the request object to the template context.
If a context processor and a view both set the same key, which wins?
- The processor value
- Neither — it raises an error
- The view value, merged last
- The first one alphabetically
Answer: The view value, merged last. Django merges processor dicts first, then the view's context, so the view value overrides the processor's.
What happens if a context processor returns None instead of a dict?
- Django ignores it silently
- Django raises a TypeError trying to merge it
- The page renders blank
- It returns an empty dict automatically
Answer: Django raises a TypeError trying to merge it. Django expects a dict to merge; returning None raises a TypeError.
Which built-in processor exposes flash {{ messages }}?
- django.template.context_processors.static
- django.contrib.messages.context_processors.messages
- django.template.context_processors.request
- django.contrib.auth.context_processors.auth
Answer: django.contrib.messages.context_processors.messages. The messages context processor exposes the messages framework's flash messages.
When should you prefer a context processor over passing a variable in the view?
- When the value is needed across many or all templates
- When the value is specific to one page
- When the value requires a heavy database query
- Never — views are always better
Answer: When the value is needed across many or all templates. Use a processor for site-wide values; pass page-specific values directly from the view.
Why should context processors stay fast?
- Templates cache them once
- They only run in DEBUG mode
- They run on every request, so a slow one slows every page
- Django limits them to one per project
Answer: They run on every request, so a slow one slows every page. Because Django calls every processor on each request, a slow processor slows down the whole site.
Continue this course
- Previous: Checkpoint: Build a Django REST API
- Next: Custom User Model — Swap in your own user model with AUTH_USER_MODEL and a manager
- Quick reference: Django cheat sheet
Frequently asked questions
What is a context processor in Django?
A context processor is a small function that takes the current request and returns a dictionary of variables. Django runs every registered context processor while rendering a template and merges all of their dictionaries into the template's context. The result is that the variables they return are available in every template automatically, without each view having to pass them. Typical uses are the site name, the current year, navigation links, or the logged-in user.
Where do I register a context processor?
You register it in the TEMPLATES setting in settings.py. Inside the OPTIONS dictionary there is a context_processors list, and you add the dotted import path of your function to that list — for example 'myapp.context_processors.site_settings'. Django imports and calls every function in that list for each request, so the order in the list also controls the order in which their dictionaries are merged.
What are Django's built-in context processors?
Django ships several. The most common are django.template.context_processors.request, which exposes the request object as {{ request }}; django.contrib.auth.context_processors.auth, which exposes {{ user }} and {{ perms }}; django.contrib.messages.context_processors.messages, which exposes flash messages; and django.template.context_processors.debug and .static. A fresh project enables the request, auth, and messages processors by default.
When should I use a context processor instead of passing a variable in the view?
Use a context processor when a variable is genuinely needed across many or all templates — site-wide settings, the current user, a shopping cart count, or footer data. If a value is specific to one page, pass it from that view instead. Context processors run on every request, so keep them fast and avoid heavy database queries inside them, because a slow processor slows down every single page on the site.