100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Python

Context and Template Tags

Understand how Django builds the template context (including automatic context processors) and how to write custom template tags and filters for reusable presentation logic.

Views & URLsIntermediate10 min readJul 10, 2026
Analogies

How Context Reaches a Template

The context is the dictionary-like object that maps names to values available inside a template; a view builds it explicitly (render(request, 'page.html', {'articles': articles})) or a generic CBV builds it automatically via get_context_data(). Beyond what the view supplies, Django merges in output from context processors — functions listed in TEMPLATES['OPTIONS']['context_processors'] such as django.template.context_processors.request or django.contrib.auth.context_processors.auth — which is why {{ user }} and {{ request }} are available in every template without every single view explicitly adding them. Context processors only run for templates rendered through RequestContext (which render() and generic CBVs use by default), not for Template().render() called with a plain Context.

🏏

Cricket analogy: It's like ground-staff prep before a match: the head groundsman (view) sets the pitch specifics, but the venue's standing facilities like floodlights and the scoreboard (context processors) are already wired in for every match automatically.

Writing Custom Template Tags

When built-in tags and filters can't express a piece of presentation logic, you register custom ones in <app>/templatetags/<module>.py, starting with register = template.Library(). Simple tags that just return a computed value use the @register.simple_tag decorator (optionally takes_context=True to access the current context), while tags needing to render a mini-template with their own logic use @register.inclusion_tag('partial.html'), which renders a specified template using the dictionary the function returns. Both must be loaded explicitly in any template that uses them via {% load <module> %} near the top of the file, after any {% extends %} line.

🏏

Cricket analogy: It's like a broadcaster building a custom graphic overlay — say a 'Manhattan chart' — that isn't a standard scoreboard element, registered once and then dropped into any match broadcast that needs it.

python
# blog/templatetags/blog_extras.py
from django import template

register = template.Library()

@register.simple_tag
def reading_time(word_count, wpm=200):
    minutes = max(1, round(word_count / wpm))
    return f'{minutes} min read'

@register.inclusion_tag('blog/partials/author_card.html')
def author_card(author):
    return {'author': author, 'article_count': author.article_set.count()}

{# in a template #}
{% load blog_extras %}
{% reading_time article.word_count %}
{% author_card article.author %}

Context Managers: `{% with %}` and RequestContext Internals

{% with total=items|length %}...{% endwith %} caches an expensive or repeated expression's result under a local name for the duration of the block, avoiding recomputation every time that expression is referenced in the template — useful when a filter chain or property access is non-trivial. Internally, RequestContext maintains context as a stack of dictionaries pushed and popped as template blocks and includes are entered and exited, which is why a variable set inside a {% for %} loop or {% with %} block doesn't leak out and shadow a same-named variable defined earlier in the template. Understanding this stack behavior matters when debugging why a variable appears unexpectedly unset or reset outside the block that defined it.

🏏

Cricket analogy: It's like a scorer caching the current run rate once per over instead of recalculating it after every single ball, then discarding that cached figure once the over ends.

{% load %} tags are per-template, not inherited automatically through {% extends %} — a child template that uses a custom tag must its own {% load %} line even if the parent template already loaded that same tag library.

Custom context processors run on every single template render that uses RequestContext across the entire project, so putting an expensive database query inside one (rather than in a specific view) silently adds that query's cost to every page load, including ones that never use the data.

  • The template context merges view-supplied data with output from context processors (auth, request, messages, etc.).
  • Context processors only apply to templates rendered via RequestContext, which render() and generic CBVs use by default.
  • @register.simple_tag returns a computed value; @register.inclusion_tag renders a sub-template from a returned dict.
  • Custom tags/filters live in <app>/templatetags/<module>.py and must be loaded per-template with {% load %}.
  • {% with %}...{% endwith %} caches an expression's result under a local name for that block only.
  • Context behaves as a stack: variables set inside {% for %} or {% with %} don't leak outside their block.
  • Avoid expensive queries inside context processors since they run on every RequestContext-rendered page.

Practice what you learned

Was this page helpful?

Topics covered

#Python#DjangoStudyNotes#WebDevelopment#ContextAndTemplateTags#Context#Template#Tags#Reaches#StudyNotes#SkillVeris