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.