diff --git a/docs/overview.txt b/docs/overview.txt
index a84a9ea624..9f0b1db521 100644
--- a/docs/overview.txt
+++ b/docs/overview.txt
@@ -195,20 +195,10 @@ Generally, a view retrieves data according to the parameters, loads a template
and renders the template with the retrieved data. Here's an example view for
article_detail from above::
- from django.models.news import articles
-
def article_detail(request, year, month, article_id):
# Use the Django API to find an object matching the URL criteria.
- try:
- a = articles.get_object(pub_date__year=year, pub_date__month=month, pk=article_id)
- except articles.ArticleDoesNotExist:
- raise Http404
- t = template_loader.get_template('news/article_detail')
- c = Context(request, {
- 'article': a,
- })
- content = t.render(c)
- return HttpResponse(content)
+ a = get_object_or_404(articles, pub_date__year=year, pub_date__month=month, pk=article_id)
+ return render_to_response('news/article_detail', {'article': a})
This example uses Django's template system, which has several key features.
@@ -261,7 +251,6 @@ template has to define only what's unique to that template.
Here's what the "base" template might look like::
-
{% block title %}{% endblock %}
diff --git a/docs/tutorial03.txt b/docs/tutorial03.txt
index 875bd7b86a..d34c480661 100644
--- a/docs/tutorial03.txt
+++ b/docs/tutorial03.txt
@@ -192,14 +192,14 @@ you want to change the way the page looks, you'll have to edit this Python code.
So let's use Django's template system to separate the design from Python::
from django.core import template_loader
- from django.core.extensions import DjangoContext as Context
+ from django.core.template import Context
from django.models.polls import polls
from django.utils.httpwrappers import HttpResponse
def index(request):
latest_poll_list = polls.get_list(order_by=['-pub_date'], limit=5)
t = template_loader.get_template('polls/index')
- c = Context(request, {
+ c = Context({
'latest_poll_list': latest_poll_list,
})
return HttpResponse(t.render(c))
@@ -242,6 +242,27 @@ Put the following code in that template::
Load the page in your Web browser, and you should see a bulleted-list
containing the "What's up" poll from Tutorial 1.
+A shortcut: render_to_response()
+--------------------------------
+
+It's a very common idiom to load a template, fill a context and return an
+``HttpResponse`` object with the result of the rendered template. Django
+provides a shortcut. Here's the full ``index()`` view, rewritten::
+
+ from django.core.extensions import render_to_response
+ from django.models.polls import polls
+
+ def index(request):
+ latest_poll_list = polls.get_list(order_by=['-pub_date'], limit=5)
+ return render_to_response('polls/index', {'latest_poll_list': latest_poll_list})
+
+Note that we no longer need to import ``template_loader``, ``Context`` or
+``HttpResponse``.
+
+The ``render_to_response()`` function takes a template name as its first
+argument and a dictionary as its optional second argument. It returns an
+``HttpResponse`` object of the given template rendered with the given context.
+
Raising 404
===========
@@ -254,15 +275,41 @@ for a given poll. Here's the view::
p = polls.get_object(pk=poll_id)
except polls.PollDoesNotExist:
raise Http404
- t = template_loader.get_template('polls/detail')
- c = Context(request, {
- 'poll': p,
- })
- return HttpResponse(t.render(c))
+ return render_to_response('polls/detail', {'poll': p})
The new concept here: The view raises the ``django.core.exceptions.Http404``
exception if a poll with the requested ID doesn't exist.
+A shortcut: get_object_or_404()
+-------------------------------
+
+It's a very common idiom to use ``get_object()`` and raise ``Http404`` if the
+object doesn't exist. Django provides a shortcut. Here's the ``detail()`` view,
+rewritten:
+
+ from django.core.extensions import get_object_or_404
+ def detail(request, poll_id):
+ p = get_object_or_404(polls, pk=poll_id)
+ return render_to_response('polls/detail', {'poll': p})
+
+The ``get_object_or_404()`` function takes a Django model module as its first
+argument and an arbitrary number of keyword arguments, which it passes to the
+module's ``get_object()`` function. It raises ``Http404`` if the object doesn't
+exist.
+
+.. admonition:: Philosophy
+
+ Why do we use a helper function ``get_object_or_404()`` instead of
+ automatically catching the ``*DoesNotExist`` exceptions at a higher level,
+ or having the model API raise ``Http404`` instead of ``*DoesNotExist``?
+
+ Because that would couple the model layer to the view layer. One of the
+ foremost design goals of Django is to maintain loose coupling.
+
+There's also a ``get_list_or_404()`` function, which works just as
+``get_object_or_404()`` -- except using ``get_list()`` instead of
+``get_object()``. It raises ``Http404`` if the list is empty.
+
Write a 404 (page not found) view
=================================
diff --git a/docs/tutorial04.txt b/docs/tutorial04.txt
index c16ccb5b69..3e6d6205bb 100644
--- a/docs/tutorial04.txt
+++ b/docs/tutorial04.txt
@@ -48,27 +48,20 @@ included this line::
So let's create a ``vote()`` function in ``myproject/apps/polls/views/polls.py``::
- from django.core import template_loader
- from django.core.extensions import DjangoContext as Context
+ from django.core.extensions import get_object_or_404, render_to_response
from django.models.polls import choices, polls
- from django.utils.httpwrappers import HttpResponse, HttpResponseRedirect
- from django.core.exceptions import Http404
+ from django.utils.httpwrappers import HttpResponseRedirect
def vote(request, poll_id):
- try:
- p = polls.get_object(pk=poll_id)
- except polls.PollDoesNotExist:
- raise Http404
+ p = get_object_or_404(polls, pk=poll_id)
try:
selected_choice = p.get_choice(pk=request.POST['choice'])
except (KeyError, choices.ChoiceDoesNotExist):
# Redisplay the poll voting form.
- t = template_loader.get_template('polls/detail')
- c = Context(request, {
+ return render_to_response('polls/detail', {
'poll': p,
'error_message': "You didn't select a choice.",
})
- return HttpResponse(t.render(c))
else:
selected_choice.votes += 1
selected_choice.save()
@@ -109,15 +102,8 @@ After somebody votes in a poll, the ``vote()`` view redirects to the results
page for the poll. Let's write that view::
def results(request, poll_id):
- try:
- p = polls.get_object(pk=poll_id)
- except polls.PollDoesNotExist:
- raise Http404
- t = template_loader.get_template('polls/results')
- c = Context(request, {
- 'poll': p,
- })
- return HttpResponse(t.render(c))
+ p = get_object_or_404(polls, pk=poll_id)
+ return render_to_response('polls/results', {'poll': p})
This is almost exactly the same as the ``detail()`` view from `Tutorial 3`_.
The only difference is the template name. We'll fix this redundancy later.