Procesory kontekstu

Sposób używania tzw. procesorów kontekstu w Django jest niestety niezbyt intuicyjny. Zacznijmy jednak od omówienia czym jest kontekst i procesor kontekstu w Django.
Jak wiadomo, Django oddziela warstwę prezentacji od logiki oprogramowania (oraz od danych).
Przypuśćmy, że mamy listę książek, przechowywaną w obiekcie books.

Chcemy wyświetlić ich tytuły zapisane w polu books.title.
Robimy szablon np. mytemplate.html:

<ol>
{% for b in books_list %}
<li>{{b.title}}</li>
{% endfor %}
</ol>

Jak widać, w szablonie mamy pętlę, która iteruje po danych z listy oznaczonej books_list. Ta lista musi być jakoś do szablonu przekazana. Przekazanie następuje właśnie poprzez kontekst. Może on być zwykłym słownikiem pythona.
Słownik ten musimy zdefiniować w funkcji widoku. Na przykład:

def show_books(request):
    from myproject.myapp.models import Book
    books_list = Book.objects.all()
    context = {'books_list':books_list}
    return render_to_response('mytemplate.html', context)

Jaki sens ma ten słownik? Jeśli w szablonie jest jakaś zmienna, to Django szuka w słowniku kontekstu klucza o identycznym brzmieniu i przrekazuje w to miejsce wartość ze słownika odpowiadającą temu kluczowi. W tym przypadku jest to lista książek.

Innymi słowy, jeśli chcemy, żeby coś zostało przekazane do szablonu, musimy to umieścić w słowniku. Oczywiście bywa i tak, że takie przekazywanie byłoby niewygodne. Funkcji widoku w rozbudowanym serwisie www będzie bardzo wiele, a niektóre zmienne powinny być przekazane do każdej z nich. Na przykład jeśli chcemy wyświetlić w okienku z boku na każdej stronie portalu nowości wydawnicze
Newest.objects.all(), to musielibyśmy je wpisywać za każdym razem do słownika kontekstu gdy wywołujemy funkcję render_to_response. Jest to nie tylko niewygodne, ale i sprzeczne z zasadą DRY (don't repeat yourself).

Do tego właśnie służą procesory kontekstu. Taki procesor umieszczamy gdzieś w pliku np: processors.py.
Jest on funkcją:

from myproject.myapp.models import Newest
 
def myproc(request):
    return {'newest': Newest.objects.all()}

Ten procesor kontekstu sprawi, że nowości będą widoczne w każdym widoku, nawet gdy nie zostaną jawnie przekazane. Ale żeby to zadziałało musimy spełnić jeszcze dwa warunki.
1. Zaglądamy do pliku settings.py. Odnajdujemy tam zmienną TEMPLATE_CONTEXT_PROCESSORS.
Domyślnie ma ona postać takiej krotki:

TEMPLATE_CONTEXT_PROCESSORS = (
    "django.core.context_processors.auth",
    "django.core.context_processors.debug",
    "django.core.context_processors.i18n",
    "django.core.context_processors.media",
)

Zmienna ta określa (jak łatwo się domyslić) procesory kontekstu, które mają być uzywane w projekcie. Oczywiście trzeba do tej krotki dodać nasz procesor:     "myapp.processors.myproc". Widać też, że domyślnie używane są pewne predefiniowane procesory, które postaram się omówić przy innej okazji. Pozostał tam nam jeszcze drugi warunek by procesor kontekstu działał.

2. Jeśli w wywołaniu render_to_response użyjemy jako kontekstu zwykłego słownika, to procesory kontekstu (nawet te wymienione w settings.py) nie będą działać. To jest właśnie ten bardzo nieintuicyjny element, o którym pisałem na początku. Musimy przy wywołaniu render_to_response użyć obiektu RequestContext. Można to zrobić na kilka sposobów:
a)

return render_to_response('template.html', 
                 {'foo':'bar'},
                 context_instance=RequestContext(request)
                 )

b)
    context = RequestContext(request, {'foo':'bar'})    
    return render_to_response('template.html', context)

c)
return render_to_response('template.html', RequestContext(request, {'foo':'bar'}))

Gwoli ścisłości należy dodać, że w wywołaniu render_to_response można też zmusić do działania procesory kontekstu nie wymienione w settings.py:

return render_to_response('template.html', 
          {'foo':'bar'},
          context_instance=RequestContext(request, processors = extra_processors)
          )