skip to content

In Django, how would you write a context processor that exposes the current store's branding to every template, and when does it run?

level: middleimportance: must knowfreq 52%

answer

  1. a function of the request
  2. returns a dict to merge
  3. registered under OPTIONS
  4. needs a request at render time
  5. the view's own variables win

basics

~20 s

Write a function that takes the request and returns a dict such as {'store': ...}, add its dotted path to OPTIONS['context_processors'], and it runs on every render that has a request — render(), render_to_string(..., request=request), generic views — but never on a request-less render.

solid answer

~40 s

A context processor is a plain function `def store_branding(request): return {"store": ...}` registered by dotted path in the engine's `OPTIONS["context_processors"]`. When a template is rendered **with a request** — `render()`, `render_to_string(name, context, request=request)`, `TemplateResponse` and the generic views all pass one — Django builds a `RequestContext`, calls every configured processor and merges the returned dicts, so `{{ store.name }}` and `{{ store.logo.url }}` work in every template without each view passing them. It runs on **every** such render, so keep it cheap: wrap database work in `SimpleLazyObject` so it runs only if a template actually reads the value. It does **not** run for `render_to_string()` without `request` or for `Template(...).render(Context(...))`, a common cause of 'variable empty in emails'. If the view passes a variable with the same name, the view's value wins.

code

python · 7 lines
python
from django.template.loader import render_to_string

# processors run: request is passed, so {{ store.name }} is filled
html = render_to_string("stores/receipt_email.html", {"order": order}, request=request)

# processors do NOT run: no request, so store is missing unless passed
html = render_to_string("stores/receipt_email.html", {"order": order, "store": order.store})

go deeper

for a junior

Recall that a context processor is a function taking the request and returning a dict, listed in OPTIONS context_processors.

for a middle

Explain that processors run only through RequestContext, name which rendering calls supply a request, and note that the view's variables win on a name clash.

for a senior

Keep processors lazy and cheap, spot request-less renders in emails and jobs that silently lose the values, and avoid name clashes.

for a principal

Decide what deserves to be global: a small, stable set of processor variables versus explicit view context, weighing convenience against hidden per-request cost.

## The problem context processors solve A multi-store shop serves several storefronts from one Django project, each with its own name, logo and accent colour chosen by the request's host. Every page — home, product, cart, account — needs that branding in its layout. Passing `store` from every view is repetitive and easy to forget. A **context processor** injects it automatically. ## Writing one A context processor is any callable that accepts the `HttpRequest` and returns a `dict`: ```python # stores/context_processors.py from django.utils.functional import SimpleLazyObject from .models import Store def store_branding(request): host = request.get_host().partition(":")[0] return { "store": SimpleLazyObject( lambda: Store.objects.filter(domain=host).first() ), } ``` Register it by dotted path: ```python TEMPLATES = [{ "BACKEND": "django.template.backends.django.DjangoTemplates", "APP_DIRS": True, "OPTIONS": { "context_processors": [ "django.template.context_processors.request", "django.contrib.auth.context_processors.auth", "django.contrib.messages.context_processors.messages", "stores.context_processors.store_branding", ], }, }] ``` Every template rendered with a request can now use `{{ store.name }}`. ## When it runs — and when it does not Processors run through **`RequestContext`**, which the `DjangoTemplates` backend builds whenever a template is rendered with a request: | Rendering call | Processors run? | |---|---| | `render(request, "x.html", ctx)` | yes | | generic class-based views, `TemplateResponse` | yes | | `render_to_string("x.html", ctx, request=request)` | yes | | `render_to_string("x.html", ctx)` | **no** | | `Template("...").render(Context(ctx))` | **no** | The request-less forms are typical for emails and background jobs, which is why a branded email template often renders with an empty `store`. Pass `request=` when you have one, or put the values in the context explicitly when you don't. ## Precedence and built-ins 1. The processors' output is merged **below** the view's context: if a view passes `store`, its value wins over the processor's. 2. Processors run in the order listed; a later one overwrites an earlier one's key. 3. The CSRF processor is **built in**: `RequestContext` always runs it, which is how `{% csrf_token %}` works without listing it. 4. The `startproject` defaults add `request`, `user` and `perms`, and `messages`. ## Keeping it cheap A processor runs on **every** request-bound render, including pages that never show the value and fragments rendered separately. Guidelines: - Wrap queries in **`SimpleLazyObject`** (or a lazily evaluated callable) so the query runs only if a template touches the value; the `user` that Django's `auth` processor exposes is `request.user`, which the authentication middleware already makes lazy for the same reason. - Avoid per-call work that repeats within one request; if middleware already resolved the store onto the request, just return that attribute. - Return small, stable names (`store`, not `data`) to avoid clashes with view variables. ## When not to use one - Data only a few pages need belongs in those views' context. - Anything that needs arguments from the template is a custom template tag's job. - With the Jinja2 backend, Django's docs discourage processors in favour of environment globals, because Jinja2 templates can call functions directly.

  • A view passes {'store': featured_store} and the store_branding processor also returns store. Which one does the template see?
    The view's. When rendering with a request, Django creates a `RequestContext` for the processors and pushes the view's dictionary on top, so values from the view override processor output of the same name. That is deliberate: a view can always override a global default.
  • Why does {% csrf_token %} work even though no csrf processor is listed in OPTIONS?
    `RequestContext` always runs a built-in CSRF context processor in addition to the configured ones, so `csrf_token` is present in any template rendered with a request. It is absent on request-less renders, which is why forms rendered via `render_to_string()` without `request` lose the token.

saying these in an interview costs you the question

  • Context processors run for every template render, with or without a request.
  • Processor values override variables the view passes with the same name.
  • A context processor receives the template context, not the request.
  • Context processors are cheap, so querying the database in one is free.
  • render_to_string always applies context processors.