From 97cb07c3a10ff0e584a260a7ee1001614691eb1d Mon Sep 17 00:00:00 2001 From: Jacob Kaplan-Moss Date: Sat, 23 Aug 2008 22:25:40 +0000 Subject: Massive reorganization of the docs. See the new docs online at http://docs.djangoproject.com/. git-svn-id: http://code.djangoproject.com/svn/django/trunk@8506 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- docs/tutorial04.txt | 304 ---------------------------------------------------- 1 file changed, 304 deletions(-) delete mode 100644 docs/tutorial04.txt (limited to 'docs/tutorial04.txt') diff --git a/docs/tutorial04.txt b/docs/tutorial04.txt deleted file mode 100644 index 78f954d632..0000000000 --- a/docs/tutorial04.txt +++ /dev/null @@ -1,304 +0,0 @@ -===================================== -Writing your first Django app, part 4 -===================================== - -This tutorial begins where `Tutorial 3`_ left off. We're continuing the Web-poll -application and will focus on simple form processing and cutting down our code. - -Write a simple form -=================== - -Let's update our poll detail template ("polls/detail.html") from the last -tutorial, so that the template contains an HTML ``
`` element:: - -

{{ poll.question }}

- - {% if error_message %}

{{ error_message }}

{% endif %} - - - {% for choice in poll.choice_set.all %} - -
- {% endfor %} - -
- -A quick rundown: - - * The above template displays a radio button for each poll choice. The - ``value`` of each radio button is the associated poll choice's ID. The - ``name`` of each radio button is ``"choice"``. That means, when somebody - selects one of the radio buttons and submits the form, it'll send the - POST data ``choice=3``. This is HTML Forms 101. - - * We set the form's ``action`` to ``/polls/{{ poll.id }}/vote/``, and we - set ``method="post"``. Using ``method="post"`` (as opposed to - ``method="get"``) is very important, because the act of submitting this - form will alter data server-side. Whenever you create a form that alters - data server-side, use ``method="post"``. This tip isn't specific to - Django; it's just good Web development practice. - - * ``forloop.counter`` indicates how many times the ``for`` tag has - gone through its loop. For more information, see `the - documentation for the "for" tag`_. - -.. _the documentation for the "for" tag: ../templates/#for - -Now, let's create a Django view that handles the submitted data and does -something with it. Remember, in `Tutorial 3`_, we created a URLconf for the -polls application that includes this line:: - - (r'^(?P\d+)/vote/$', 'mysite.polls.views.vote'), - -So let's create a ``vote()`` function in ``mysite/polls/views.py``:: - - from django.shortcuts import get_object_or_404, render_to_response - from django.http import HttpResponseRedirect - from django.core.urlresolvers import reverse - from mysite.polls.models import Choice, Poll - # ... - def vote(request, poll_id): - p = get_object_or_404(Poll, pk=poll_id) - try: - selected_choice = p.choice_set.get(pk=request.POST['choice']) - except (KeyError, Choice.DoesNotExist): - # Redisplay the poll voting form. - return render_to_response('polls/detail.html', { - 'poll': p, - 'error_message': "You didn't select a choice.", - }) - else: - selected_choice.votes += 1 - selected_choice.save() - # Always return an HttpResponseRedirect after successfully dealing - # with POST data. This prevents data from being posted twice if a - # user hits the Back button. - return HttpResponseRedirect(reverse('mysite.polls.views.results', args=(p.id,))) - -This code includes a few things we haven't covered yet in this tutorial: - - * ``request.POST`` is a dictionary-like object that lets you access - submitted data by key name. In this case, ``request.POST['choice']`` - returns the ID of the selected choice, as a string. ``request.POST`` - values are always strings. - - Note that Django also provides ``request.GET`` for accessing GET data - in the same way -- but we're explicitly using ``request.POST`` in our - code, to ensure that data is only altered via a POST call. - - * ``request.POST['choice']`` will raise ``KeyError`` if ``choice`` wasn't - provided in POST data. The above code checks for ``KeyError`` and - redisplays the poll form with an error message if ``choice`` isn't given. - - * After incrementing the choice count, the code returns an - ``HttpResponseRedirect`` rather than a normal ``HttpResponse``. - ``HttpResponseRedirect`` takes a single argument: the URL to which the - user will be redirected (see the following point for how we construct - the URL in this case). - - As the Python comment above points out, you should always return an - ``HttpResponseRedirect`` after successfully dealing with POST data. This - tip isn't specific to Django; it's just good Web development practice. - - * We are using the ``reverse()`` function in the ``HttpResponseRedirect`` - constructor in this example. This function helps avoid having to - hardcode a URL in the view function. It is given the name of the view - that we want to pass control to and the variable portion of the URL - pattern that points to that view. In this case, using the URLConf we set - up in Tutorial 3, this ``reverse()`` call will return a string like :: - - '/polls/3/results/' - - ... where the ``3`` is the value of ``p.id``. This redirected URL will - then call the ``'results'`` view to display the final page. Note that - you need to use the full name of the view here (including the prefix). - - For more information about ``reverse()``, see the `URL dispatcher`_ - documentation. - -As mentioned in Tutorial 3, ``request`` is a ``HTTPRequest`` object. For more -on ``HTTPRequest`` objects, see the `request and response documentation`_. - -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): - p = get_object_or_404(Poll, pk=poll_id) - return render_to_response('polls/results.html', {'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. - -Now, create a ``results.html`` template:: - -

{{ poll.question }}

- - - -Now, go to ``/polls/1/`` in your browser and vote in the poll. You should see a -results page that gets updated each time you vote. If you submit the form -without having chosen a choice, you should see the error message. - -.. _request and response documentation: ../request_response/ -.. _URL dispatcher: ../url_dispatch#reverse - -Use generic views: Less code is better -====================================== - -The ``detail()`` (from `Tutorial 3`_) and ``results()`` views are stupidly -simple -- and, as mentioned above, redundant. The ``index()`` view (also from -Tutorial 3), which displays a list of polls, is similar. - -These views represent a common case of basic Web development: getting data from -the database according to a parameter passed in the URL, loading a template and -returning the rendered template. Because this is so common, Django provides a -shortcut, called the "generic views" system. - -Generic views abstract common patterns to the point where you don't even need -to write Python code to write an app. - -Let's convert our poll app to use the generic views system, so we can delete a -bunch of our own code. We'll just have to take a few steps to make the -conversion. - -.. admonition:: Why the code-shuffle? - - Generally, when writing a Django app, you'll evaluate whether generic views - are a good fit for your problem, and you'll use them from the beginning, - rather than refactoring your code halfway through. But this tutorial - intentionally has focused on writing the views "the hard way" until now, to - focus on core concepts. - - You should know basic math before you start using a calculator. - -First, open the polls/urls.py URLconf. It looks like this, according to the -tutorial so far:: - - from django.conf.urls.defaults import * - - urlpatterns = patterns('mysite.polls.views', - (r'^$', 'index'), - (r'^(?P\d+)/$', 'detail'), - (r'^(?P\d+)/results/$', 'results'), - (r'^(?P\d+)/vote/$', 'vote'), - ) - -Change it like so:: - - from django.conf.urls.defaults import * - from mysite.polls.models import Poll - - info_dict = { - 'queryset': Poll.objects.all(), - } - - urlpatterns = patterns('', - (r'^$', 'django.views.generic.list_detail.object_list', info_dict), - (r'^(?P\d+)/$', 'django.views.generic.list_detail.object_detail', info_dict), - url(r'^(?P\d+)/results/$', 'django.views.generic.list_detail.object_detail', dict(info_dict, template_name='polls/results.html'), 'poll_results'), - (r'^(?P\d+)/vote/$', 'mysite.polls.views.vote'), - ) - -We're using two generic views here: ``object_list`` and ``object_detail``. -Respectively, those two views abstract the concepts of "display a list of -objects" and "display a detail page for a particular type of object." - - * Each generic view needs to know what data it will be acting upon. This - data is provided in a dictionary. The ``queryset`` key in this dictionary - points to the list of objects to be manipulated by the generic view. - - * The ``object_detail`` generic view expects the ID value captured - from the URL to be called ``"object_id"``, so we've changed ``poll_id`` to - ``object_id`` for the generic views. - - * We've added a name, ``poll_results``, to the results view so that we - have a way to refer to its URL later on (see the documentation about - `naming URL patterns`_ for information). We're also using the `url()`_ - function from ``django.conf.urls.defaults`` here. It's a good habit to - use ``url()`` when you are providing a pattern name like this. - -.. _naming URL patterns: ../url_dispatch/#naming-url-patterns -.. _url(): ../url_dispatch/#url - -By default, the ``object_detail`` generic view uses a template called -``/_detail.html``. In our case, it'll use the template -``"polls/poll_detail.html"``. Thus, rename your ``polls/detail.html`` template to -``polls/poll_detail.html``, and change the ``render_to_response()`` line in -``vote()``. - -Similarly, the ``object_list`` generic view uses a template called -``/_list.html``. Thus, rename ``polls/index.html`` to -``polls/poll_list.html``. - -Because we have more than one entry in the URLconf that uses ``object_detail`` -for the polls app, we manually specify a template name for the results view: -``template_name='polls/results.html'``. Otherwise, both views would use the same -template. Note that we use ``dict()`` to return an altered dictionary in place. - -.. note:: ``all()`` is lazy - - It might look a little frightening to see ``Poll.objects.all()`` being used - in a detail view which only needs one ``Poll`` object, but don't worry; - ``Poll.objects.all()`` is actually a special object called a ``QuerySet``, - which is "lazy" and doesn't hit your database until it absolutely has to. By - the time the database query happens, the ``object_detail`` generic view will - have narrowed its scope down to a single object, so the eventual query will - only select one row from the database. - - If you'd like to know more about how that works, The Django database API - documentation `explains the lazy nature of QuerySet objects`_. - -.. _explains the lazy nature of QuerySet objects: ../db-api/#querysets-are-lazy - -In previous parts of the tutorial, the templates have been provided with a context -that contains the ``poll`` and ``latest_poll_list`` context variables. However, -the generic views provide the variables ``object`` and ``object_list`` as context. -Therefore, you need to change your templates to match the new context variables. -Go through your templates, and modify any reference to ``latest_poll_list`` to -``object_list``, and change any reference to ``poll`` to ``object``. - -You can now delete the ``index()``, ``detail()`` and ``results()`` views -from ``polls/views.py``. We don't need them anymore -- they have been replaced -by generic views. - -The ``vote()`` view is still required. However, it must be modified to match -the new context variables. In the ``render_to_response()`` call, rename the -``poll`` context variable to ``object``. - -The last thing to do is fix the URL handling to account for the use of generic -views. In the vote view above, we used the ``reverse()`` function to avoid -hard-coding our URLs. Now that we've switched to a generic view, we'll need to -change the ``reverse()`` call to point back to our new generic view. We can't -simply use the view function anymore -- generic views can be (and are) used -multiple times -- but we can use the name we've given:: - - return HttpResponseRedirect(reverse('poll_results', args=(p.id,))) - -Run the server, and use your new polling app based on generic views. - -For full details on generic views, see the `generic views documentation`_. - -.. _generic views documentation: ../generic_views/ - -Coming soon -=========== - -The tutorial ends here for the time being. But check back soon for the next -installments: - - * Advanced form processing - * Using the RSS framework - * Using the cache framework - * Using the comments framework - * Advanced admin features: Permissions - * Advanced admin features: Custom JavaScript - -In the meantime, you can read through the rest of the `Django documentation`_ -and start writing your own applications. - -.. _Tutorial 3: ../tutorial03/ -.. _Django documentation: http://www.djangoproject.com/documentation/ -- cgit v1.3