diff --git a/docs/tutorial01.txt b/docs/tutorial01.txt index f5ce2572e5..d59488da0b 100644 --- a/docs/tutorial01.txt +++ b/docs/tutorial01.txt @@ -24,7 +24,7 @@ Initial setup If this is your first time using Django, you'll have to take care of some initial setup. -Run the command ``django-admin.py startproject myproject``. That'll create a +Run the command ``django-admin.py startproject myproject``. This will create a ``myproject`` directory in your current directory. (``django-admin.py`` should be on your system path if you installed Django via @@ -125,9 +125,10 @@ database's connection parameters: database's interactive prompt. While you're editing ``settings.py``, take note of the ``INSTALLED_APPS`` -setting. That variable holds the names of all Django applications that are -activated in this Django instance. Apps can be used in multiple projects, -and you can distribute them. +setting towards the bottom of the file. That variable holds the names of all +Django applications that are activated in this Django instance. Apps can be +used in multiple projects, and you can package and distribute them for use +by others in their projects. By default, ``INSTALLED_APPS`` contains the following apps, all of which come with Django:: @@ -285,12 +286,9 @@ to include the string ``'myproject.polls'``. So it'll look like this:: 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.sites', - 'myproject.polls', + 'myproject.polls' ) -(Don't forget the trailing comma, because of Python's rule about single-value -tuples: Without a trailing comma, Python wouldn't know this was a tuple.) - Now Django knows ``myproject`` includes the ``polls`` app. Let's run another command:: python manage.py sql polls @@ -331,6 +329,12 @@ Note the following: quotes. The author of this tutorial runs PostgreSQL, so the example output is in PostgreSQL syntax. + * The `sql` command doesn't actually run the SQL in your database - it just + prints it to the screen so that you can see what SQL Django thinks is required. + If you wanted to, you could copy and paste this SQL into your database prompt. + However, as we will see shortly, Django provides an easier way of committing + the SQL to the database. + If you're interested, also run the following commands: * ``python manage.py sqlinitialdata polls`` -- Outputs any initial data @@ -343,8 +347,8 @@ If you're interested, also run the following commands: * ``python manage.py sqlindexes polls`` -- Outputs the ``CREATE INDEX`` statements for this app. - * ``python manage.py sqlall polls`` -- A combination of 'sql' and - 'sqlinitialdata'. + * ``python manage.py sqlall polls`` -- A combination of all the SQL from + the 'sql', 'sqlinitialdata', and 'sqlindexes' commands. Looking at the output of those commands can help you understand what's actually happening under the hood. @@ -353,9 +357,11 @@ Now, run ``syncdb`` again to create those model tables in your database:: python manage.py syncdb -As a review, the ``syncdb`` command creates the tables for all apps in -``INSTALLED_APPS`` that don't already exist in your database. So you can run it -again and again, and it'll always just create the tables that don't exist. +The ``syncdb`` command runs the sql from 'sqlall' on your database for all apps +in ``INSTALLED_APPS`` that don't already exist in your database. This creates +all the tables, initial data and indexes for any apps you have added to your +project since the last time you ran syncdb. ``syncdb`` can be called as often +as you like, and it will only ever create the tables that don't exist. Read the `django-admin.py documentation`_ for full information on what the ``manage.py`` utility can do. @@ -454,10 +460,11 @@ representations are used throughout Django's automatically-generated admin. Note these are normal Python methods. Let's add a custom method, just for demonstration:: + import datetime + # ... class Poll(models.Model): # ... def was_published_today(self): - import datetime return self.pub_date.date() == datetime.date.today() Note the addition of ``import datetime`` to reference Python's standard @@ -488,8 +495,6 @@ Let's jump back into the Python interactive shell by running Traceback (most recent call last): ... DoesNotExist: Poll does not exist for {'id': 2} - >>> Poll.objects.filter(question__startswith='What') - [What's up?] # Lookup by a primary key is the most common case, so Django provides a # shortcut for primary-key exact lookups. @@ -502,14 +507,15 @@ Let's jump back into the Python interactive shell by running >>> p.was_published_today() False - # Give the Poll a couple of Choices. Each one of these method calls does an - # INSERT statement behind the scenes and returns the new Choice object. + # Give the Poll a couple of Choices. The create call constructs a new + # choice object, does the INSERT statement, adds the choice to the set + # of available choices and returns the new Choice object. >>> p = Poll.objects.get(pk=1) - >>> p.choice_set.add(choice='Not much', votes=0) + >>> p.choice_set.create(choice='Not much', votes=0) Not much - >>> p.choice_set.add(choice='The sky', votes=0) + >>> p.choice_set.create(choice='The sky', votes=0) The sky - >>> c = p.choice_set.add(choice='Just hacking again', votes=0) + >>> c = p.choice_set.create(choice='Just hacking again', votes=0) # Choice objects have API access to their related Poll objects. >>> c.poll @@ -518,7 +524,7 @@ Let's jump back into the Python interactive shell by running # And vice versa: Poll objects get access to Choice objects. >>> p.choice_set.all() [Not much, The sky, Just hacking again] - >>> p.choice_set.all().count() + >>> p.choice_set.count() 3 # The API automatically follows relationships as far as you need. diff --git a/docs/tutorial02.txt b/docs/tutorial02.txt index 2737f0dfbc..3b7060a52c 100644 --- a/docs/tutorial02.txt +++ b/docs/tutorial02.txt @@ -31,22 +31,13 @@ The Django admin site is not activated by default -- it's an opt-in thing. To activate the admin site for your installation, do these three things: * Add ``"django.contrib.admin"`` to your ``INSTALLED_APPS`` setting. - * Run the command ``python manage.py install admin``. This will create an - extra database table that the admin needs. + * Run ``python manage.py syncdb``. Since you have added a new application + to ``INSTALLED_APPS``, the database tables need to be updated. * Edit your ``myproject/urls.py`` file and uncomment the line below "Uncomment this for admin:". This file is a URLconf; we'll dig into URLconfs in the next tutorial. For now, all you need to know is that it maps URL roots to applications. -Create a user account -===================== - -Run the following command to create a superuser account for your admin site:: - - python manage.py createsuperuser - -The script will prompt you for a username, e-mail address and password (twice). - Start the development server ============================ @@ -96,7 +87,8 @@ creating an empty class means "give this object an admin interface using all the default options." Now reload the Django admin page to see your changes. Note that you don't have -to restart the development server -- it auto-reloads code. +to restart the development server -- the server will auto-reloads your project, +so any modifications code will be seen immediately in your browser. Explore the free admin functionality ==================================== diff --git a/docs/tutorial03.txt b/docs/tutorial03.txt index e7cc9f7161..b26614a5ee 100644 --- a/docs/tutorial03.txt +++ b/docs/tutorial03.txt @@ -74,25 +74,27 @@ Time for an example. Edit ``myproject/urls.py`` so it looks like this:: urlpatterns = patterns('', (r'^polls/$', 'myproject.polls.views.index'), - (r'^polls/(\d+)/$', 'myproject.polls.views.detail'), - (r'^polls/(\d+)/results/$', 'myproject.polls.views.results'), - (r'^polls/(\d+)/vote/$', 'myproject.polls.views.vote'), + (r'^polls/(?P\d+)/$', 'myproject.polls.views.detail'), + (r'^polls/(?P\d+)/results/$', 'myproject.polls.views.results'), + (r'^polls/(?P\d+)/vote/$', 'myproject.polls.views.vote'), ) This is worth a review. When somebody requests a page from your Web site -- say, "/polls/23/", Django will load this Python module, because it's pointed to by the ``ROOT_URLCONF`` setting. It finds the variable named ``urlpatterns`` and traverses the regular expressions in order. When it finds a regular -expression that matches -- ``r'^polls/(\d+)/$'`` -- it loads the +expression that matches -- ``r'^polls/(?P\d+)/$'`` -- it loads the associated Python package/module: ``myproject.polls.views.detail``. That corresponds to the function ``detail()`` in ``myproject/polls/views.py``. Finally, it calls that ``detail()`` function like so:: detail(request=, poll_id='23') -The ``poll_id='23'`` part comes from ``(\d+)``. Using parenthesis around a +The ``poll_id='23'`` part comes from ``(?P\d+)``. Using parenthesis around a pattern "captures" the text matched by that pattern and sends it as an argument -to the view function. +to the view function; the ``?P`` defines the name that will be used to +identify the matched pattern; and \d+ is a regular experession to match a sequence of +digits (i.e., a number). Because the URL patterns are regular expressions, there really is no limit on what you can do with them. And there's no need to add URL cruft such as @@ -185,29 +187,29 @@ in Tutorial 1. Here's one stab at the ``index()`` view, which displays the latest 5 poll questions in the system, separated by commas, according to publication date:: - from django.models.polls import polls - from django.http import HttpResponse + from myproject.polls.models import Poll + from django.http import HttpResponse - def index(request): - latest_poll_list = polls.get_list(order_by=['-pub_date'], limit=5) - output = ', '.join([p.question for p in latest_poll_list]) - return HttpResponse(output) + def index(request): + latest_poll_list = Poll.objects.all().order_by('-pub_date') + output = ', '.join([p.question for p in latest_poll_list]) + return HttpResponse(output) There's a problem here, though: The page's design is hard-coded in the view. If 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.template import Context, loader - from django.models.polls import polls - from django.http import HttpResponse + from django.template import Context, loader + from myproject.polls.models import Poll + from django.http import HttpResponse - def index(request): - latest_poll_list = polls.get_list(order_by=['-pub_date'], limit=5) - t = loader.get_template('polls/index') - c = Context({ - 'latest_poll_list': latest_poll_list, - }) - return HttpResponse(t.render(c)) + def index(request): + latest_poll_list = Poll.objects.all().order_by('-pub_date') + t = loader.get_template('polls/index') + c = Context({ + 'latest_poll_list': latest_poll_list, + }) + return HttpResponse(t.render(c)) That code loads the template called "polls/index" and passes it a context. The context is a dictionary mapping template variable names to Python objects. @@ -254,11 +256,11 @@ 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.shortcuts import render_to_response - from django.models.polls import polls + from django.shortcuts import render_to_response + from myproject.polls.models import Poll - def index(request): - latest_poll_list = polls.get_list(order_by=['-pub_date'], limit=5) + def index(request): + latest_poll_list = Poll.objects.all().order_by('-pub_date') return render_to_response('polls/index', {'latest_poll_list': latest_poll_list}) Note that we no longer need to import ``loader``, ``Context`` or @@ -275,12 +277,13 @@ Now, let's tackle the poll detail view -- the page that displays the question for a given poll. Here's the view:: from django.http import Http404 - def detail(request, poll_id): - try: - p = polls.get_object(pk=poll_id) - except polls.PollDoesNotExist: - raise Http404 - return render_to_response('polls/detail', {'poll': p}) + # ... + def detail(request, poll_id): + try: + p = Poll.objects.get(pk=poll_id) + except Poll.DoesNotExist: + raise Http404 + return render_to_response('polls/detail', {'poll': p}) The new concept here: The view raises the ``django.http.Http404`` exception if a poll with the requested ID doesn't exist. @@ -292,9 +295,10 @@ 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.shortcuts import get_object_or_404 + from django.shortcuts import render_to_response, get_object_or_404 + # ... def detail(request, poll_id): - p = get_object_or_404(polls, pk=poll_id) + p = get_object_or_404(Poll, 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 diff --git a/docs/tutorial04.txt b/docs/tutorial04.txt index 90e3c5d4d9..7dd9943ac9 100644 --- a/docs/tutorial04.txt +++ b/docs/tutorial04.txt @@ -18,7 +18,7 @@ template contains an HTML ``
`` element:: {% if error_message %}

{{ error_message }}

{% endif %} - {% for choice in poll.get_choice_list %} + {% for choice in poll.choice_set.all %}
{% endfor %} @@ -41,22 +41,22 @@ A quick rundown: Django; it's just good Web development practice. Now, let's create a Django view that handles the submitted data and does -something with it. Remember, in `Tutorial 3`_, we create a URLconf that -included this line:: +something with it. Remember, in `Tutorial 3`_, we create a URLconf for the +polls application that includes this line:: - (r'^polls/(?P\d+)/vote/$', 'myproject.polls.views.vote'), + (r'^(?P\d+)/vote/$', 'myproject.polls.views.vote'), So let's create a ``vote()`` function in ``myproject/polls/views.py``:: from django.shortcuts import get_object_or_404, render_to_response - from django.http import HttpResponseRedirect + from django.http import Http404,HttpResponseRedirect from myproject.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.filter(pk=request.POST['choice']) - except (KeyError, choices.ChoiceDoesNotExist): + except (KeyError, Choice.DoesNotExist): # Redisplay the poll voting form. return render_to_response('polls/detail', { 'poll': p, @@ -113,7 +113,7 @@ Now, create a ``results.html`` template::

{{ poll.question }}

    - {% for choice in poll.get_choice_list %} + {% for choice in poll.choice_set.all %}
  • {{ choice.choice }} -- {{ choice.votes }} vote{{ choice.votes|pluralize }}
  • {% endfor %}
@@ -185,37 +185,45 @@ 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 which model its acting on. This - is done using a QuerySet. + * 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 that the ID value captured - from the URL is called ``"object_id"``, so we've changed ``poll_id`` to + * 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. By default, the ``object_detail`` generic view uses a template called -``/_detail``. In our case, it'll use the template -``"polls/polls_detail"``. Thus, rename your ``polls/detail.html`` template to -``polls/polls_detail.html``, and change the ``render_to_response()`` line in +``/_detail``. In our case, it'll use the template +``"polls/poll_detail"``. 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``. Thus, rename ``polls/index.html`` to -``polls/polls_list.html``. +``/_list``. Thus, rename ``poll/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'``. Otherwise, both views would use the same template. Note that we use ``dict()`` to return an altered dictionary in place. -The generic views pass ``object`` and ``object_list`` to their templates, so -change your templates so that ``latest_poll_list`` becomes ``object_list`` and -``poll`` becomes ``object``. +In previous versions 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``. -In the ``vote()`` view, change the template call from ``polls/detail`` to -``polls/polls_detail``, and pass ``object`` in the context instead of ``poll``. +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. -Finally, you can delete the ``index()``, ``detail()`` and ``results()`` views -from ``polls/views.py``. We don't need them anymore. +The ``vote()`` view is still required. However, it must be modified to match +the new templates and context variables. Change the template call from ``polls/detail`` +to ``polls/polls_detail``, and pass ``object`` in the context instead of ``poll``. + +Run the server, and use your new polling app based on generic views. For full details on generic views, see the `generic views documentation`_.