summaryrefslogtreecommitdiff
path: root/parts/django/docs/intro
diff options
context:
space:
mode:
Diffstat (limited to 'parts/django/docs/intro')
-rw-r--r--parts/django/docs/intro/_images/admin01.pngbin0 -> 18233 bytes
-rw-r--r--parts/django/docs/intro/_images/admin02.pngbin0 -> 64260 bytes
-rw-r--r--parts/django/docs/intro/_images/admin02t.pngbin0 -> 24726 bytes
-rw-r--r--parts/django/docs/intro/_images/admin03.pngbin0 -> 75434 bytes
-rw-r--r--parts/django/docs/intro/_images/admin03t.pngbin0 -> 28131 bytes
-rw-r--r--parts/django/docs/intro/_images/admin04.pngbin0 -> 57718 bytes
-rw-r--r--parts/django/docs/intro/_images/admin04t.pngbin0 -> 22806 bytes
-rw-r--r--parts/django/docs/intro/_images/admin05.pngbin0 -> 28875 bytes
-rw-r--r--parts/django/docs/intro/_images/admin05t.pngbin0 -> 22754 bytes
-rw-r--r--parts/django/docs/intro/_images/admin06.pngbin0 -> 22780 bytes
-rw-r--r--parts/django/docs/intro/_images/admin06t.pngbin0 -> 18156 bytes
-rw-r--r--parts/django/docs/intro/_images/admin07.pngbin0 -> 19804 bytes
-rw-r--r--parts/django/docs/intro/_images/admin08.pngbin0 -> 31552 bytes
-rw-r--r--parts/django/docs/intro/_images/admin08t.pngbin0 -> 23883 bytes
-rw-r--r--parts/django/docs/intro/_images/admin09.pngbin0 -> 16318 bytes
-rw-r--r--parts/django/docs/intro/_images/admin10.pngbin0 -> 10914 bytes
-rw-r--r--parts/django/docs/intro/_images/admin11.pngbin0 -> 33563 bytes
-rw-r--r--parts/django/docs/intro/_images/admin11t.pngbin0 -> 26365 bytes
-rw-r--r--parts/django/docs/intro/_images/admin12.pngbin0 -> 12682 bytes
-rw-r--r--parts/django/docs/intro/_images/admin13.pngbin0 -> 22062 bytes
-rw-r--r--parts/django/docs/intro/_images/admin13t.pngbin0 -> 18690 bytes
-rw-r--r--parts/django/docs/intro/_images/admin14.pngbin0 -> 28987 bytes
-rw-r--r--parts/django/docs/intro/_images/admin14t.pngbin0 -> 23460 bytes
-rw-r--r--parts/django/docs/intro/index.txt36
-rw-r--r--parts/django/docs/intro/install.txt84
-rw-r--r--parts/django/docs/intro/overview.txt324
-rw-r--r--parts/django/docs/intro/tutorial01.txt690
-rw-r--r--parts/django/docs/intro/tutorial02.txt465
-rw-r--r--parts/django/docs/intro/tutorial03.txt546
-rw-r--r--parts/django/docs/intro/tutorial04.txt346
-rw-r--r--parts/django/docs/intro/whatsnext.txt231
31 files changed, 2722 insertions, 0 deletions
diff --git a/parts/django/docs/intro/_images/admin01.png b/parts/django/docs/intro/_images/admin01.png
new file mode 100644
index 0000000..28f14d6
--- /dev/null
+++ b/parts/django/docs/intro/_images/admin01.png
Binary files differ
diff --git a/parts/django/docs/intro/_images/admin02.png b/parts/django/docs/intro/_images/admin02.png
new file mode 100644
index 0000000..4b49ebb
--- /dev/null
+++ b/parts/django/docs/intro/_images/admin02.png
Binary files differ
diff --git a/parts/django/docs/intro/_images/admin02t.png b/parts/django/docs/intro/_images/admin02t.png
new file mode 100644
index 0000000..d7519d1
--- /dev/null
+++ b/parts/django/docs/intro/_images/admin02t.png
Binary files differ
diff --git a/parts/django/docs/intro/_images/admin03.png b/parts/django/docs/intro/_images/admin03.png
new file mode 100644
index 0000000..635226c
--- /dev/null
+++ b/parts/django/docs/intro/_images/admin03.png
Binary files differ
diff --git a/parts/django/docs/intro/_images/admin03t.png b/parts/django/docs/intro/_images/admin03t.png
new file mode 100644
index 0000000..94273cb
--- /dev/null
+++ b/parts/django/docs/intro/_images/admin03t.png
Binary files differ
diff --git a/parts/django/docs/intro/_images/admin04.png b/parts/django/docs/intro/_images/admin04.png
new file mode 100644
index 0000000..982420a
--- /dev/null
+++ b/parts/django/docs/intro/_images/admin04.png
Binary files differ
diff --git a/parts/django/docs/intro/_images/admin04t.png b/parts/django/docs/intro/_images/admin04t.png
new file mode 100644
index 0000000..a2ec8bb
--- /dev/null
+++ b/parts/django/docs/intro/_images/admin04t.png
Binary files differ
diff --git a/parts/django/docs/intro/_images/admin05.png b/parts/django/docs/intro/_images/admin05.png
new file mode 100644
index 0000000..b424393
--- /dev/null
+++ b/parts/django/docs/intro/_images/admin05.png
Binary files differ
diff --git a/parts/django/docs/intro/_images/admin05t.png b/parts/django/docs/intro/_images/admin05t.png
new file mode 100644
index 0000000..a5da950
--- /dev/null
+++ b/parts/django/docs/intro/_images/admin05t.png
Binary files differ
diff --git a/parts/django/docs/intro/_images/admin06.png b/parts/django/docs/intro/_images/admin06.png
new file mode 100644
index 0000000..5f24d4e
--- /dev/null
+++ b/parts/django/docs/intro/_images/admin06.png
Binary files differ
diff --git a/parts/django/docs/intro/_images/admin06t.png b/parts/django/docs/intro/_images/admin06t.png
new file mode 100644
index 0000000..fb65e0a
--- /dev/null
+++ b/parts/django/docs/intro/_images/admin06t.png
Binary files differ
diff --git a/parts/django/docs/intro/_images/admin07.png b/parts/django/docs/intro/_images/admin07.png
new file mode 100644
index 0000000..b21022f
--- /dev/null
+++ b/parts/django/docs/intro/_images/admin07.png
Binary files differ
diff --git a/parts/django/docs/intro/_images/admin08.png b/parts/django/docs/intro/_images/admin08.png
new file mode 100644
index 0000000..ddac57e
--- /dev/null
+++ b/parts/django/docs/intro/_images/admin08.png
Binary files differ
diff --git a/parts/django/docs/intro/_images/admin08t.png b/parts/django/docs/intro/_images/admin08t.png
new file mode 100644
index 0000000..83773bb
--- /dev/null
+++ b/parts/django/docs/intro/_images/admin08t.png
Binary files differ
diff --git a/parts/django/docs/intro/_images/admin09.png b/parts/django/docs/intro/_images/admin09.png
new file mode 100644
index 0000000..ba7de1b
--- /dev/null
+++ b/parts/django/docs/intro/_images/admin09.png
Binary files differ
diff --git a/parts/django/docs/intro/_images/admin10.png b/parts/django/docs/intro/_images/admin10.png
new file mode 100644
index 0000000..07a9bf3
--- /dev/null
+++ b/parts/django/docs/intro/_images/admin10.png
Binary files differ
diff --git a/parts/django/docs/intro/_images/admin11.png b/parts/django/docs/intro/_images/admin11.png
new file mode 100644
index 0000000..6c583fd
--- /dev/null
+++ b/parts/django/docs/intro/_images/admin11.png
Binary files differ
diff --git a/parts/django/docs/intro/_images/admin11t.png b/parts/django/docs/intro/_images/admin11t.png
new file mode 100644
index 0000000..af792b8
--- /dev/null
+++ b/parts/django/docs/intro/_images/admin11t.png
Binary files differ
diff --git a/parts/django/docs/intro/_images/admin12.png b/parts/django/docs/intro/_images/admin12.png
new file mode 100644
index 0000000..aac5c0d
--- /dev/null
+++ b/parts/django/docs/intro/_images/admin12.png
Binary files differ
diff --git a/parts/django/docs/intro/_images/admin13.png b/parts/django/docs/intro/_images/admin13.png
new file mode 100644
index 0000000..49a5950
--- /dev/null
+++ b/parts/django/docs/intro/_images/admin13.png
Binary files differ
diff --git a/parts/django/docs/intro/_images/admin13t.png b/parts/django/docs/intro/_images/admin13t.png
new file mode 100644
index 0000000..7dc01e1
--- /dev/null
+++ b/parts/django/docs/intro/_images/admin13t.png
Binary files differ
diff --git a/parts/django/docs/intro/_images/admin14.png b/parts/django/docs/intro/_images/admin14.png
new file mode 100644
index 0000000..b1f4a54
--- /dev/null
+++ b/parts/django/docs/intro/_images/admin14.png
Binary files differ
diff --git a/parts/django/docs/intro/_images/admin14t.png b/parts/django/docs/intro/_images/admin14t.png
new file mode 100644
index 0000000..86c3acc
--- /dev/null
+++ b/parts/django/docs/intro/_images/admin14t.png
Binary files differ
diff --git a/parts/django/docs/intro/index.txt b/parts/django/docs/intro/index.txt
new file mode 100644
index 0000000..bc61be7
--- /dev/null
+++ b/parts/django/docs/intro/index.txt
@@ -0,0 +1,36 @@
+Getting started
+===============
+
+New to Django? Or to Web development in general? Well, you came to the right
+place: read this material to quickly get up and running.
+
+.. toctree::
+ :maxdepth: 1
+
+ overview
+ install
+ tutorial01
+ tutorial02
+ tutorial03
+ tutorial04
+ whatsnext
+
+.. seealso::
+
+ If you're new to Python_, you might want to start by getting an idea of what
+ the language is like. Django is 100% Python, so if you've got minimal
+ comfort with Python you'll probably get a lot more out of Django.
+
+ If you're new to programming entirely, you might want to start with this
+ `list of Python resources for non-programmers`_
+
+ If you already know a few other languages and want to get up to speed with
+ Python quickly, we recommend `Dive Into Python`_ (also available in a
+ `dead-tree version`_). If that's not quite your style, there are quite
+ a few other `books about Python`_.
+
+ .. _python: http://python.org/
+ .. _list of Python resources for non-programmers: http://wiki.python.org/moin/BeginnersGuide/NonProgrammers
+ .. _dive into python: http://diveintopython.org/
+ .. _dead-tree version: http://www.amazon.com/exec/obidos/ASIN/1590593561/ref=nosim/jacobian20
+ .. _books about Python: http://wiki.python.org/moin/PythonBooks \ No newline at end of file
diff --git a/parts/django/docs/intro/install.txt b/parts/django/docs/intro/install.txt
new file mode 100644
index 0000000..327686f
--- /dev/null
+++ b/parts/django/docs/intro/install.txt
@@ -0,0 +1,84 @@
+Quick install guide
+===================
+
+Before you can use Django, you'll need to get it installed. We have a
+:doc:`complete installation guide </topics/install>` that covers all the
+possibilities; this guide will guide you to a simple, minimal installation
+that'll work while you walk through the introduction.
+
+Install Python
+--------------
+
+Being a Python Web framework, Django requires Python. It works with any Python
+version from 2.4 to 2.7 (due to backwards
+incompatibilities in Python 3.0, Django does not currently work with
+Python 3.0; see :doc:`the Django FAQ </faq/install>` for more
+information on supported Python versions and the 3.0 transition), but we recommend installing Python 2.5 or later. If you do so, you won't need to set up a database just yet: Python 2.5 or later includes a lightweight database called SQLite_.
+
+.. _sqlite: http://sqlite.org/
+
+Get Python at http://www.python.org. If you're running Linux or Mac OS X, you
+probably already have it installed.
+
+.. admonition:: Django on Jython
+
+ If you use Jython_ (a Python implementation for the Java platform), you'll
+ need to follow a few additional steps. See :doc:`/howto/jython` for details.
+
+.. _jython: http://www.jython.org/
+
+You can verify that Python's installed by typing ``python`` from your shell; you should see something like::
+
+ Python 2.5.1 (r251:54863, Jan 17 2008, 19:35:17)
+ [GCC 4.0.1 (Apple Inc. build 5465)] on darwin
+ Type "help", "copyright", "credits" or "license" for more information.
+ >>>
+
+Set up a database
+-----------------
+
+If you installed Python 2.5 or later, you can skip this step for now.
+
+If not, or if you'd like to work with a "large" database engine like PostgreSQL,
+MySQL, or Oracle, consult the :ref:`database installation information
+<database-installation>`.
+
+Remove any old versions of Django
+---------------------------------
+
+If you are upgrading your installation of Django from a previous version, you
+will need to :ref:`uninstall the old Django version before installing the new
+version <removing-old-versions-of-django>`.
+
+Install Django
+--------------
+
+You've got three easy options to install Django:
+
+ * Install a version of Django :doc:`provided by your operating system
+ distribution </misc/distributions>`. This is the quickest option for those
+ who have operating systems that distribute Django.
+
+ * :ref:`Install an official release <installing-official-release>`. This
+ is the best approach for users who want a stable version number and aren't
+ concerned about running a slightly older version of Django.
+
+ * :ref:`Install the latest development version
+ <installing-development-version>`. This is best for users who want the
+ latest-and-greatest features and aren't afraid of running brand-new code.
+
+.. admonition:: Always refer to the documentation that corresponds to the
+ version of Django you're using!
+
+ If you do either of the first two steps, keep an eye out for parts of the
+ documentation marked **new in development version**. That phrase flags
+ features that are only available in development versions of Django, and
+ they likely won't work with an official release.
+
+That's it!
+----------
+
+That's it -- you can now :doc:`move onto the tutorial </intro/tutorial01>`.
+
+
+
diff --git a/parts/django/docs/intro/overview.txt b/parts/django/docs/intro/overview.txt
new file mode 100644
index 0000000..34572a6
--- /dev/null
+++ b/parts/django/docs/intro/overview.txt
@@ -0,0 +1,324 @@
+==================
+Django at a glance
+==================
+
+Because Django was developed in a fast-paced newsroom environment, it was
+designed to make common Web-development tasks fast and easy. Here's an informal
+overview of how to write a database-driven Web app with Django.
+
+The goal of this document is to give you enough technical specifics to
+understand how Django works, but this isn't intended to be a tutorial or
+reference -- but we've got both! When you're ready to start a project, you can
+:doc:`start with the tutorial </intro/tutorial01>` or :doc:`dive right into more
+detailed documentation </topics/index>`.
+
+Design your model
+=================
+
+Although you can use Django without a database, it comes with an
+object-relational mapper in which you describe your database layout in Python
+code.
+
+The :doc:`data-model syntax </topics/db/models>` offers many rich ways of
+representing your models -- so far, it's been solving two years' worth of
+database-schema problems. Here's a quick example, which might be saved in
+the file ``mysite/news/models.py``::
+
+ class Reporter(models.Model):
+ full_name = models.CharField(max_length=70)
+
+ def __unicode__(self):
+ return self.full_name
+
+ class Article(models.Model):
+ pub_date = models.DateTimeField()
+ headline = models.CharField(max_length=200)
+ content = models.TextField()
+ reporter = models.ForeignKey(Reporter)
+
+ def __unicode__(self):
+ return self.headline
+
+Install it
+==========
+
+Next, run the Django command-line utility to create the database tables
+automatically:
+
+.. code-block:: bash
+
+ manage.py syncdb
+
+The :djadmin:`syncdb` command looks at all your available models and creates
+tables in your database for whichever tables don't already exist.
+
+Enjoy the free API
+==================
+
+With that, you've got a free, and rich, :doc:`Python API </topics/db/queries>` to
+access your data. The API is created on the fly, no code generation necessary::
+
+ # Import the models we created from our "news" app
+ >>> from news.models import Reporter, Article
+
+ # No reporters are in the system yet.
+ >>> Reporter.objects.all()
+ []
+
+ # Create a new Reporter.
+ >>> r = Reporter(full_name='John Smith')
+
+ # Save the object into the database. You have to call save() explicitly.
+ >>> r.save()
+
+ # Now it has an ID.
+ >>> r.id
+ 1
+
+ # Now the new reporter is in the database.
+ >>> Reporter.objects.all()
+ [<Reporter: John Smith>]
+
+ # Fields are represented as attributes on the Python object.
+ >>> r.full_name
+ 'John Smith'
+
+ # Django provides a rich database lookup API.
+ >>> Reporter.objects.get(id=1)
+ <Reporter: John Smith>
+ >>> Reporter.objects.get(full_name__startswith='John')
+ <Reporter: John Smith>
+ >>> Reporter.objects.get(full_name__contains='mith')
+ <Reporter: John Smith>
+ >>> Reporter.objects.get(id=2)
+ Traceback (most recent call last):
+ ...
+ DoesNotExist: Reporter matching query does not exist.
+
+ # Create an article.
+ >>> from datetime import datetime
+ >>> a = Article(pub_date=datetime.now(), headline='Django is cool',
+ ... content='Yeah.', reporter=r)
+ >>> a.save()
+
+ # Now the article is in the database.
+ >>> Article.objects.all()
+ [<Article: Django is cool>]
+
+ # Article objects get API access to related Reporter objects.
+ >>> r = a.reporter
+ >>> r.full_name
+ 'John Smith'
+
+ # And vice versa: Reporter objects get API access to Article objects.
+ >>> r.article_set.all()
+ [<Article: Django is cool>]
+
+ # The API follows relationships as far as you need, performing efficient
+ # JOINs for you behind the scenes.
+ # This finds all articles by a reporter whose name starts with "John".
+ >>> Article.objects.filter(reporter__full_name__startswith="John")
+ [<Article: Django is cool>]
+
+ # Change an object by altering its attributes and calling save().
+ >>> r.full_name = 'Billy Goat'
+ >>> r.save()
+
+ # Delete an object with delete().
+ >>> r.delete()
+
+A dynamic admin interface: it's not just scaffolding -- it's the whole house
+============================================================================
+
+Once your models are defined, Django can automatically create a professional,
+production ready :doc:`administrative interface </ref/contrib/admin/index>` -- a Web
+site that lets authenticated users add, change and delete objects. It's as easy
+as registering your model in the admin site::
+
+ # In models.py...
+
+ from django.db import models
+
+ class Article(models.Model):
+ pub_date = models.DateTimeField()
+ headline = models.CharField(max_length=200)
+ content = models.TextField()
+ reporter = models.ForeignKey(Reporter)
+
+
+ # In admin.py in the same directory...
+
+ import models
+ from django.contrib import admin
+
+ admin.site.register(models.Article)
+
+The philosophy here is that your site is edited by a staff, or a client, or
+maybe just you -- and you don't want to have to deal with creating backend
+interfaces just to manage content.
+
+One typical workflow in creating Django apps is to create models and get the
+admin sites up and running as fast as possible, so your staff (or clients) can
+start populating data. Then, develop the way data is presented to the public.
+
+Design your URLs
+================
+
+A clean, elegant URL scheme is an important detail in a high-quality Web
+application. Django encourages beautiful URL design and doesn't put any cruft
+in URLs, like ``.php`` or ``.asp``.
+
+To design URLs for an app, you create a Python module called a :doc:`URLconf
+</topics/http/urls>`. A table of contents for your app, it contains a simple mapping
+between URL patterns and Python callback functions. URLconfs also serve to
+decouple URLs from Python code.
+
+Here's what a URLconf might look like for the ``Reporter``/``Article``
+example above::
+
+ from django.conf.urls.defaults import *
+
+ urlpatterns = patterns('',
+ (r'^articles/(\d{4})/$', 'news.views.year_archive'),
+ (r'^articles/(\d{4})/(\d{2})/$', 'news.views.month_archive'),
+ (r'^articles/(\d{4})/(\d{2})/(\d+)/$', 'news.views.article_detail'),
+ )
+
+The code above maps URLs, as simple regular expressions, to the location of
+Python callback functions ("views"). The regular expressions use parenthesis to
+"capture" values from the URLs. When a user requests a page, Django runs
+through each pattern, in order, and stops at the first one that matches the
+requested URL. (If none of them matches, Django calls a special-case 404 view.)
+This is blazingly fast, because the regular expressions are compiled at load
+time.
+
+Once one of the regexes matches, Django imports and calls the given view, which
+is a simple Python function. Each view gets passed a request object --
+which contains request metadata -- and the values captured in the regex.
+
+For example, if a user requested the URL "/articles/2005/05/39323/", Django
+would call the function ``news.views.article_detail(request,
+'2005', '05', '39323')``.
+
+Write your views
+================
+
+Each view is responsible for doing one of two things: Returning an
+:class:`~django.http.HttpResponse` object containing the content for the
+requested page, or raising an exception such as :class:`~django.http.Http404`.
+The rest is up to you.
+
+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
+``year_archive`` from above::
+
+ def year_archive(request, year):
+ a_list = Article.objects.filter(pub_date__year=year)
+ return render_to_response('news/year_archive.html', {'year': year, 'article_list': a_list})
+
+This example uses Django's :doc:`template system </topics/templates>`, which has
+several powerful features but strives to stay simple enough for non-programmers
+to use.
+
+Design your templates
+=====================
+
+The code above loads the ``news/year_archive.html`` template.
+
+Django has a template search path, which allows you to minimize redundancy among
+templates. In your Django settings, you specify a list of directories to check
+for templates. If a template doesn't exist in the first directory, it checks the
+second, and so on.
+
+Let's say the ``news/article_detail.html`` template was found. Here's what that
+might look like:
+
+.. code-block:: html+django
+
+ {% extends "base.html" %}
+
+ {% block title %}Articles for {{ year }}{% endblock %}
+
+ {% block content %}
+ <h1>Articles for {{ year }}</h1>
+
+ {% for article in article_list %}
+ <p>{{ article.headline }}</p>
+ <p>By {{ article.reporter.full_name }}</p>
+ <p>Published {{ article.pub_date|date:"F j, Y" }}</p>
+ {% endfor %}
+ {% endblock %}
+
+Variables are surrounded by double-curly braces. ``{{ article.headline }}``
+means "Output the value of the article's headline attribute." But dots aren't
+used only for attribute lookup: They also can do dictionary-key lookup, index
+lookup and function calls.
+
+Note ``{{ article.pub_date|date:"F j, Y" }}`` uses a Unix-style "pipe" (the "|"
+character). This is called a template filter, and it's a way to filter the value
+of a variable. In this case, the date filter formats a Python datetime object in
+the given format (as found in PHP's date function; yes, there is one good idea
+in PHP).
+
+You can chain together as many filters as you'd like. You can write custom
+filters. You can write custom template tags, which run custom Python code behind
+the scenes.
+
+Finally, Django uses the concept of "template inheritance": That's what the
+``{% extends "base.html" %}`` does. It means "First load the template called
+'base', which has defined a bunch of blocks, and fill the blocks with the
+following blocks." In short, that lets you dramatically cut down on redundancy
+in templates: each template has to define only what's unique to that template.
+
+Here's what the "base.html" template might look like:
+
+.. code-block:: html+django
+
+ <html>
+ <head>
+ <title>{% block title %}{% endblock %}</title>
+ </head>
+ <body>
+ <img src="sitelogo.gif" alt="Logo" />
+ {% block content %}{% endblock %}
+ </body>
+ </html>
+
+Simplistically, it defines the look-and-feel of the site (with the site's logo),
+and provides "holes" for child templates to fill. This makes a site redesign as
+easy as changing a single file -- the base template.
+
+It also lets you create multiple versions of a site, with different base
+templates, while reusing child templates. Django's creators have used this
+technique to create strikingly different cell-phone editions of sites -- simply
+by creating a new base template.
+
+Note that you don't have to use Django's template system if you prefer another
+system. While Django's template system is particularly well-integrated with
+Django's model layer, nothing forces you to use it. For that matter, you don't
+have to use Django's database API, either. You can use another database
+abstraction layer, you can read XML files, you can read files off disk, or
+anything you want. Each piece of Django -- models, views, templates -- is
+decoupled from the next.
+
+This is just the surface
+========================
+
+This has been only a quick overview of Django's functionality. Some more useful
+features:
+
+ * A :doc:`caching framework </topics/cache>` that integrates with memcached
+ or other backends.
+
+ * A :doc:`syndication framework </ref/contrib/syndication>` that makes
+ creating RSS and Atom feeds as easy as writing a small Python class.
+
+ * More sexy automatically-generated admin features -- this overview barely
+ scratched the surface.
+
+The next obvious steps are for you to `download Django`_, read :doc:`the
+tutorial </intro/tutorial01>` and join `the community`_. Thanks for your
+interest!
+
+.. _download Django: http://www.djangoproject.com/download/
+.. _the community: http://www.djangoproject.com/community/
diff --git a/parts/django/docs/intro/tutorial01.txt b/parts/django/docs/intro/tutorial01.txt
new file mode 100644
index 0000000..560070b
--- /dev/null
+++ b/parts/django/docs/intro/tutorial01.txt
@@ -0,0 +1,690 @@
+=====================================
+Writing your first Django app, part 1
+=====================================
+
+Let's learn by example.
+
+Throughout this tutorial, we'll walk you through the creation of a basic
+poll application.
+
+It'll consist of two parts:
+
+ * A public site that lets people view polls and vote in them.
+ * An admin site that lets you add, change and delete polls.
+
+We'll assume you have :doc:`Django installed </intro/install>` already. You can
+tell Django is installed by running the Python interactive interpreter and
+typing ``import django``. If that command runs successfully, with no errors,
+Django is installed.
+
+.. admonition:: Where to get help:
+
+ If you're having trouble going through this tutorial, please post a message
+ to `django-users`__ or drop by `#django on irc.freenode.net`__ to chat
+ with other Django users who might be able to help.
+
+__ http://groups.google.com/group/django-users
+__ irc://irc.freenode.net/django
+
+Creating a project
+==================
+
+If this is your first time using Django, you'll have to take care of some
+initial setup. Namely, you'll need to auto-generate some code that establishes a
+Django :term:`project` -- a collection of settings for an instance of Django,
+including database configuration, Django-specific options and
+application-specific settings.
+
+From the command line, ``cd`` into a directory where you'd like to store your
+code, then run the command ``django-admin.py startproject mysite``. This will
+create a ``mysite`` directory in your current directory.
+
+.. admonition:: Script name may differ in distribution packages
+
+ If you installed Django using a Linux distribution's package manager
+ (e.g. apt-get or yum) ``django-admin.py`` may have been renamed to
+ ``django-admin``. You may continue through this documentation by omitting
+ ``.py`` from each command.
+
+.. admonition:: Mac OS X permissions
+
+ If you're using Mac OS X, you may see the message "permission denied" when
+ you try to run ``django-admin.py startproject``. This is because, on
+ Unix-based systems like OS X, a file must be marked as "executable" before it
+ can be run as a program. To do this, open Terminal.app and navigate (using
+ the ``cd`` command) to the directory where :doc:`django-admin.py
+ </ref/django-admin>` is installed, then run the command
+ ``chmod +x django-admin.py``.
+
+.. note::
+
+ You'll need to avoid naming projects after built-in Python or Django
+ components. In particular, this means you should avoid using names like
+ ``django`` (which will conflict with Django itself) or ``test`` (which
+ conflicts with a built-in Python package).
+
+:doc:`django-admin.py </ref/django-admin>` should be on your system path if you
+installed Django via ``python setup.py``. If it's not on your path, you can find
+it in ``site-packages/django/bin``, where ```site-packages``` is a directory
+within your Python installation. Consider symlinking to :doc:`django-admin.py
+</ref/django-admin>` from some place on your path, such as
+:file:`/usr/local/bin`.
+
+.. admonition:: Where should this code live?
+
+ If your background is in PHP, you're probably used to putting code under the
+ Web server's document root (in a place such as ``/var/www``). With Django,
+ you don't do that. It's not a good idea to put any of this Python code
+ within your Web server's document root, because it risks the possibility
+ that people may be able to view your code over the Web. That's not good for
+ security.
+
+ Put your code in some directory **outside** of the document root, such as
+ :file:`/home/mycode`.
+
+Let's look at what :djadmin:`startproject` created::
+
+ mysite/
+ __init__.py
+ manage.py
+ settings.py
+ urls.py
+
+These files are:
+
+ * :file:`__init__.py`: An empty file that tells Python that this directory
+ should be considered a Python package. (Read `more about packages`_ in the
+ official Python docs if you're a Python beginner.)
+
+ * :file:`manage.py`: A command-line utility that lets you interact with this
+ Django project in various ways. You can read all the details about
+ :file:`manage.py` in :doc:`/ref/django-admin`.
+
+ * :file:`settings.py`: Settings/configuration for this Django project.
+ :doc:`/topics/settings` will tell you all about how settings work.
+
+ * :file:`urls.py`: The URL declarations for this Django project; a "table of
+ contents" of your Django-powered site. You can read more about URLs in
+ :doc:`/topics/http/urls`.
+
+.. _more about packages: http://docs.python.org/tutorial/modules.html#packages
+
+The development server
+----------------------
+
+Let's verify this worked. Change into the :file:`mysite` directory, if you
+haven't already, and run the command ``python manage.py runserver``. You'll see
+the following output on the command line::
+
+ Validating models...
+ 0 errors found.
+
+ Django version 1.0, using settings 'mysite.settings'
+ Development server is running at http://127.0.0.1:8000/
+ Quit the server with CONTROL-C.
+
+You've started the Django development server, a lightweight Web server written
+purely in Python. We've included this with Django so you can develop things
+rapidly, without having to deal with configuring a production server -- such as
+Apache -- until you're ready for production.
+
+Now's a good time to note: DON'T use this server in anything resembling a
+production environment. It's intended only for use while developing. (We're in
+the business of making Web frameworks, not Web servers.)
+
+Now that the server's running, visit http://127.0.0.1:8000/ with your Web
+browser. You'll see a "Welcome to Django" page, in pleasant, light-blue pastel.
+It worked!
+
+.. admonition:: Changing the port
+
+ By default, the :djadmin:`runserver` command starts the development server
+ on the internal IP at port 8000.
+
+ If you want to change the server's port, pass
+ it as a command-line argument. For instance, this command starts the server
+ on port 8080:
+
+ .. code-block:: bash
+
+ python manage.py runserver 8080
+
+ If you want to change the server's IP, pass it along with the port. So to
+ listen on all public IPs (useful if you want to show off your work on other
+ computers), use:
+
+ .. code-block:: bash
+
+ python manage.py runserver 0.0.0.0:8000
+
+ Full docs for the development server can be found in the
+ :djadmin:`runserver` reference.
+
+Database setup
+--------------
+
+Now, edit :file:`settings.py`. It's a normal Python module with
+module-level variables representing Django settings. Change the
+following keys in the :setting:`DATABASES` ``'default'`` item to match
+your databases connection settings.
+
+ * :setting:`ENGINE` -- Either
+ ``'django.db.backends.postgresql_psycopg2'``,
+ ``'django.db.backends.mysql'`` or
+ ``'django.db.backends.sqlite3'``. Other backends are
+ :setting:`also available <ENGINE>`.
+
+ * :setting:`NAME` -- The name of your database. If you're using
+ SQLite, the database will be a file on your computer; in that
+ case, :setting:`NAME` should be the full absolute path,
+ including filename, of that file. If the file doesn't exist, it
+ will automatically be created when you synchronize the database
+ for the first time (see below).
+
+ When specifying the path, always use forward slashes, even on
+ Windows (e.g. ``C:/homes/user/mysite/sqlite3.db``).
+
+ * :setting:`USER` -- Your database username (not used for SQLite).
+
+ * :setting:`PASSWORD` -- Your database password (not used for
+ SQLite).
+
+ * :setting:`HOST` -- The host your database is on. Leave this as
+ an empty string if your database server is on the same physical
+ machine (not used for SQLite).
+
+If you're new to databases, we recommend simply using SQLite (by
+setting :setting:`ENGINE` to ``'django.db.backends.sqlite3'``). SQLite
+is included as part of Python 2.5 and later, so you won't need to
+install anything else.
+
+.. note::
+
+ If you're using PostgreSQL or MySQL, make sure you've created a database by
+ this point. Do that with "``CREATE DATABASE database_name;``" within your
+ database's interactive prompt.
+
+ If you're using SQLite, you don't need to create anything beforehand - the
+ database file will be created automatically when it is needed.
+
+While you're editing :file:`settings.py`, take note of the
+:setting:`INSTALLED_APPS` 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, :setting:`INSTALLED_APPS` contains the following apps, all of which
+come with Django:
+
+ * :mod:`django.contrib.auth` -- An authentication system.
+
+ * :mod:`django.contrib.contenttypes` -- A framework for content types.
+
+ * :mod:`django.contrib.sessions` -- A session framework.
+
+ * :mod:`django.contrib.sites` -- A framework for managing multiple sites
+ with one Django installation.
+
+ * :mod:`django.contrib.messages` -- A messaging framework.
+
+These applications are included by default as a convenience for the common case.
+
+Each of these applications makes use of at least one database table, though,
+so we need to create the tables in the database before we can use them. To do
+that, run the following command:
+
+.. code-block:: bash
+
+ python manage.py syncdb
+
+The :djadmin:`syncdb` command looks at the :setting:`INSTALLED_APPS` setting and
+creates any necessary database tables according to the database settings in your
+:file:`settings.py` file. You'll see a message for each database table it
+creates, and you'll get a prompt asking you if you'd like to create a superuser
+account for the authentication system. Go ahead and do that.
+
+If you're interested, run the command-line client for your database and type
+``\dt`` (PostgreSQL), ``SHOW TABLES;`` (MySQL), or ``.schema`` (SQLite) to
+display the tables Django created.
+
+.. admonition:: For the minimalists
+
+ Like we said above, the default applications are included for the common
+ case, but not everybody needs them. If you don't need any or all of them,
+ feel free to comment-out or delete the appropriate line(s) from
+ :setting:`INSTALLED_APPS` before running :djadmin:`syncdb`. The
+ :djadmin:`syncdb` command will only create tables for apps in
+ :setting:`INSTALLED_APPS`.
+
+.. _creating-models:
+
+Creating models
+===============
+
+Now that your environment -- a "project" -- is set up, you're set to start
+doing work.
+
+Each application you write in Django consists of a Python package, somewhere
+on your `Python path`_, that follows a certain convention. Django comes with a
+utility that automatically generates the basic directory structure of an app,
+so you can focus on writing code rather than creating directories.
+
+.. admonition:: Projects vs. apps
+
+ What's the difference between a project and an app? An app is a Web
+ application that does something -- e.g., a Weblog system, a database of
+ public records or a simple poll app. A project is a collection of
+ configuration and apps for a particular Web site. A project can contain
+ multiple apps. An app can be in multiple projects.
+
+Your apps can live anywhere on your `Python path`_. In this tutorial, we'll
+create our poll app in the :file:`mysite` directory for simplicity.
+
+To create your app, make sure you're in the :file:`mysite` directory and type
+this command:
+
+.. code-block:: bash
+
+ python manage.py startapp polls
+
+That'll create a directory :file:`polls`, which is laid out like this::
+
+ polls/
+ __init__.py
+ models.py
+ tests.py
+ views.py
+
+This directory structure will house the poll application.
+
+The first step in writing a database Web app in Django is to define your models
+-- essentially, your database layout, with additional metadata.
+
+.. admonition:: Philosophy
+
+ A model is the single, definitive source of data about your data. It contains
+ the essential fields and behaviors of the data you're storing. Django follows
+ the :ref:`DRY Principle <dry>`. The goal is to define your data model in one
+ place and automatically derive things from it.
+
+In our simple poll app, we'll create two models: polls and choices. A poll has
+a question and a publication date. A choice has two fields: the text of the
+choice and a vote tally. Each choice is associated with a poll.
+
+These concepts are represented by simple Python classes. Edit the
+:file:`polls/models.py` file so it looks like this::
+
+ from django.db import models
+
+ class Poll(models.Model):
+ question = models.CharField(max_length=200)
+ pub_date = models.DateTimeField('date published')
+
+ class Choice(models.Model):
+ poll = models.ForeignKey(Poll)
+ choice = models.CharField(max_length=200)
+ votes = models.IntegerField()
+
+The code is straightforward. Each model is represented by a class that
+subclasses :class:`django.db.models.Model`. Each model has a number of class
+variables, each of which represents a database field in the model.
+
+Each field is represented by an instance of a :class:`~django.db.models.Field`
+class -- e.g., :class:`~django.db.models.CharField` for character fields and
+:class:`~django.db.models.DateTimeField` for datetimes. This tells Django what
+type of data each field holds.
+
+The name of each :class:`~django.db.models.Field` instance (e.g. ``question`` or
+``pub_date`` ) is the field's name, in machine-friendly format. You'll use this
+value in your Python code, and your database will use it as the column name.
+
+You can use an optional first positional argument to a
+:class:`~django.db.models.Field` to designate a human-readable name. That's used
+in a couple of introspective parts of Django, and it doubles as documentation.
+If this field isn't provided, Django will use the machine-readable name. In this
+example, we've only defined a human-readable name for ``Poll.pub_date``. For all
+other fields in this model, the field's machine-readable name will suffice as
+its human-readable name.
+
+Some :class:`~django.db.models.Field` classes have required elements.
+:class:`~django.db.models.CharField`, for example, requires that you give it a
+:attr:`~django.db.models.Field.max_length`. That's used not only in the database
+schema, but in validation, as we'll soon see.
+
+Finally, note a relationship is defined, using
+:class:`~django.db.models.ForeignKey`. That tells Django each Choice is related
+to a single Poll. Django supports all the common database relationships:
+many-to-ones, many-to-manys and one-to-ones.
+
+.. _`Python path`: http://docs.python.org/tutorial/modules.html#the-module-search-path
+
+Activating models
+=================
+
+That small bit of model code gives Django a lot of information. With it, Django
+is able to:
+
+ * Create a database schema (``CREATE TABLE`` statements) for this app.
+ * Create a Python database-access API for accessing Poll and Choice objects.
+
+But first we need to tell our project that the ``polls`` app is installed.
+
+.. admonition:: Philosophy
+
+ Django apps are "pluggable": You can use an app in multiple projects, and
+ you can distribute apps, because they don't have to be tied to a given
+ Django installation.
+
+Edit the :file:`settings.py` file again, and change the
+:setting:`INSTALLED_APPS` setting to include the string ``'polls'``. So
+it'll look like this::
+
+ INSTALLED_APPS = (
+ 'django.contrib.auth',
+ 'django.contrib.contenttypes',
+ 'django.contrib.sessions',
+ 'django.contrib.sites',
+ 'polls'
+ )
+
+Now Django knows to include the ``polls`` app. Let's run another
+command:
+
+.. code-block:: bash
+
+ python manage.py sql polls
+
+You should see something similar to the following (the ``CREATE TABLE`` SQL
+statements for the polls app):
+
+.. code-block:: sql
+
+ BEGIN;
+ CREATE TABLE "polls_poll" (
+ "id" serial NOT NULL PRIMARY KEY,
+ "question" varchar(200) NOT NULL,
+ "pub_date" timestamp with time zone NOT NULL
+ );
+ CREATE TABLE "polls_choice" (
+ "id" serial NOT NULL PRIMARY KEY,
+ "poll_id" integer NOT NULL REFERENCES "polls_poll" ("id"),
+ "choice" varchar(200) NOT NULL,
+ "votes" integer NOT NULL
+ );
+ COMMIT;
+
+Note the following:
+
+ * The exact output will vary depending on the database you are using.
+
+ * Table names are automatically generated by combining the name of the app
+ (``polls``) and the lowercase name of the model -- ``poll`` and
+ ``choice``. (You can override this behavior.)
+
+ * Primary keys (IDs) are added automatically. (You can override this, too.)
+
+ * By convention, Django appends ``"_id"`` to the foreign key field name.
+ Yes, you can override this, as well.
+
+ * The foreign key relationship is made explicit by a ``REFERENCES``
+ statement.
+
+ * It's tailored to the database you're using, so database-specific field
+ types such as ``auto_increment`` (MySQL), ``serial`` (PostgreSQL), or
+ ``integer primary key`` (SQLite) are handled for you automatically. Same
+ goes for quoting of field names -- e.g., using double quotes or single
+ quotes. The author of this tutorial runs PostgreSQL, so the example
+ output is in PostgreSQL syntax.
+
+ * The :djadmin:`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:
+
+ * :djadmin:`python manage.py validate <validate>` -- Checks for any errors
+ in the construction of your models.
+
+ * :djadmin:`python manage.py sqlcustom polls <sqlcustom>` -- Outputs any
+ :ref:`custom SQL statements <initial-sql>` (such as table modifications or
+ constraints) that are defined for the application.
+
+ * :djadmin:`python manage.py sqlclear polls <sqlclear>` -- Outputs the
+ necessary ``DROP TABLE`` statements for this app, according to which
+ tables already exist in your database (if any).
+
+ * :djadmin:`python manage.py sqlindexes polls <sqlindexes>` -- Outputs the
+ ``CREATE INDEX`` statements for this app.
+
+ * :djadmin:`python manage.py sqlall polls <sqlall>` -- A combination of all
+ the SQL from the :djadmin:`sql`, :djadmin:`sqlcustom`, and
+ :djadmin:`sqlindexes` commands.
+
+Looking at the output of those commands can help you understand what's actually
+happening under the hood.
+
+Now, run :djadmin:`syncdb` again to create those model tables in your database:
+
+.. code-block:: bash
+
+ python manage.py syncdb
+
+The :djadmin:`syncdb` command runs the sql from 'sqlall' on your database for
+all apps in :setting:`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. :djadmin:`syncdb` can
+be called as often as you like, and it will only ever create the tables that
+don't exist.
+
+Read the :doc:`django-admin.py documentation </ref/django-admin>` for full
+information on what the ``manage.py`` utility can do.
+
+Playing with the API
+====================
+
+Now, let's hop into the interactive Python shell and play around with the free
+API Django gives you. To invoke the Python shell, use this command:
+
+.. code-block:: bash
+
+ python manage.py shell
+
+We're using this instead of simply typing "python", because ``manage.py`` sets
+up the project's environment for you. "Setting up the environment" involves two
+things:
+
+ * Putting ``polls`` on ``sys.path``. For flexibility, several pieces of
+ Django refer to projects in Python dotted-path notation (e.g.
+ ``'polls.models'``). In order for this to work, the ``polls``
+ package has to be on ``sys.path``.
+
+ We've already seen one example of this: the :setting:`INSTALLED_APPS`
+ setting is a list of packages in dotted-path notation.
+
+ * Setting the ``DJANGO_SETTINGS_MODULE`` environment variable, which gives
+ Django the path to your ``settings.py`` file.
+
+.. admonition:: Bypassing manage.py
+
+ If you'd rather not use ``manage.py``, no problem. Just make sure ``mysite``
+ and ``polls`` are at the root level on the Python path (i.e., ``import mysite``
+ and ``import polls`` work) and set the ``DJANGO_SETTINGS_MODULE`` environment
+ variable to ``mysite.settings``.
+
+ For more information on all of this, see the :doc:`django-admin.py
+ documentation </ref/django-admin>`.
+
+Once you're in the shell, explore the :doc:`database API </topics/db/queries>`::
+
+ >>> from polls.models import Poll, Choice # Import the model classes we just wrote.
+
+ # No polls are in the system yet.
+ >>> Poll.objects.all()
+ []
+
+ # Create a new Poll.
+ >>> import datetime
+ >>> p = Poll(question="What's up?", pub_date=datetime.datetime.now())
+
+ # Save the object into the database. You have to call save() explicitly.
+ >>> p.save()
+
+ # Now it has an ID. Note that this might say "1L" instead of "1", depending
+ # on which database you're using. That's no biggie; it just means your
+ # database backend prefers to return integers as Python long integer
+ # objects.
+ >>> p.id
+ 1
+
+ # Access database columns via Python attributes.
+ >>> p.question
+ "What's up?"
+ >>> p.pub_date
+ datetime.datetime(2007, 7, 15, 12, 00, 53)
+
+ # Change values by changing the attributes, then calling save().
+ >>> p.pub_date = datetime.datetime(2007, 4, 1, 0, 0)
+ >>> p.save()
+
+ # objects.all() displays all the polls in the database.
+ >>> Poll.objects.all()
+ [<Poll: Poll object>]
+
+
+Wait a minute. ``<Poll: Poll object>`` is, utterly, an unhelpful representation
+of this object. Let's fix that by editing the polls model (in the
+``polls/models.py`` file) and adding a
+:meth:`~django.db.models.Model.__unicode__` method to both ``Poll`` and
+``Choice``::
+
+ class Poll(models.Model):
+ # ...
+ def __unicode__(self):
+ return self.question
+
+ class Choice(models.Model):
+ # ...
+ def __unicode__(self):
+ return self.choice
+
+It's important to add :meth:`~django.db.models.Model.__unicode__` methods to
+your models, not only for your own sanity when dealing with the interactive
+prompt, but also because objects' representations are used throughout Django's
+automatically-generated admin.
+
+.. admonition:: Why :meth:`~django.db.models.Model.__unicode__` and not
+ :meth:`~django.db.models.Model.__str__`?
+
+ If you're familiar with Python, you might be in the habit of adding
+ :meth:`~django.db.models.Model.__str__` methods to your classes, not
+ :meth:`~django.db.models.Model.__unicode__` methods. We use
+ :meth:`~django.db.models.Model.__unicode__` here because Django models deal
+ with Unicode by default. All data stored in your database is converted to
+ Unicode when it's returned.
+
+ Django models have a default :meth:`~django.db.models.Model.__str__` method
+ that calls :meth:`~django.db.models.Model.__unicode__` and converts the
+ result to a UTF-8 bytestring. This means that ``unicode(p)`` will return a
+ Unicode string, and ``str(p)`` will return a normal string, with characters
+ encoded as UTF-8.
+
+ If all of this is jibberish to you, just remember to add
+ :meth:`~django.db.models.Model.__unicode__` methods to your models. With any
+ luck, things should Just Work for you.
+
+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):
+ return self.pub_date.date() == datetime.date.today()
+
+Note the addition of ``import datetime`` to reference Python's standard
+``datetime`` module.
+
+Save these changes and start a new Python interactive shell by running
+``python manage.py shell`` again::
+
+ >>> from polls.models import Poll, Choice
+
+ # Make sure our __unicode__() addition worked.
+ >>> Poll.objects.all()
+ [<Poll: What's up?>]
+
+ # Django provides a rich database lookup API that's entirely driven by
+ # keyword arguments.
+ >>> Poll.objects.filter(id=1)
+ [<Poll: What's up?>]
+ >>> Poll.objects.filter(question__startswith='What')
+ [<Poll: What's up?>]
+
+ # Get the poll whose year is 2007.
+ >>> Poll.objects.get(pub_date__year=2007)
+ <Poll: What's up?>
+
+ >>> Poll.objects.get(id=2)
+ Traceback (most recent call last):
+ ...
+ DoesNotExist: Poll matching query does not exist.
+
+ # Lookup by a primary key is the most common case, so Django provides a
+ # shortcut for primary-key exact lookups.
+ # The following is identical to Poll.objects.get(id=1).
+ >>> Poll.objects.get(pk=1)
+ <Poll: What's up?>
+
+ # Make sure our custom method worked.
+ >>> p = Poll.objects.get(pk=1)
+ >>> p.was_published_today()
+ False
+
+ # 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. Django creates
+ # a set to hold the "other side" of a ForeignKey relation
+ # (e.g. a poll's choices) which can be accessed via the API.
+ >>> p = Poll.objects.get(pk=1)
+
+ # Display any choices from the related object set -- none so far.
+ >>> p.choice_set.all()
+ []
+
+ # Create three choices.
+ >>> p.choice_set.create(choice='Not much', votes=0)
+ <Choice: Not much>
+ >>> p.choice_set.create(choice='The sky', votes=0)
+ <Choice: The sky>
+ >>> c = p.choice_set.create(choice='Just hacking again', votes=0)
+
+ # Choice objects have API access to their related Poll objects.
+ >>> c.poll
+ <Poll: What's up?>
+
+ # And vice versa: Poll objects get access to Choice objects.
+ >>> p.choice_set.all()
+ [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]
+ >>> p.choice_set.count()
+ 3
+
+ # The API automatically follows relationships as far as you need.
+ # Use double underscores to separate relationships.
+ # This works as many levels deep as you want; there's no limit.
+ # Find all Choices for any poll whose pub_date is in 2007.
+ >>> Choice.objects.filter(poll__pub_date__year=2007)
+ [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]
+
+ # Let's delete one of the choices. Use delete() for that.
+ >>> c = p.choice_set.filter(choice__startswith='Just hacking')
+ >>> c.delete()
+
+For more information on model relations, see :doc:`Accessing related objects
+</ref/models/relations>`. For full details on the database API, see our
+:doc:`Database API reference </topics/db/queries>`.
+
+When you're comfortable with the API, read :doc:`part 2 of this tutorial
+</intro/tutorial02>` to get Django's automatic admin working.
diff --git a/parts/django/docs/intro/tutorial02.txt b/parts/django/docs/intro/tutorial02.txt
new file mode 100644
index 0000000..c80d87d
--- /dev/null
+++ b/parts/django/docs/intro/tutorial02.txt
@@ -0,0 +1,465 @@
+=====================================
+Writing your first Django app, part 2
+=====================================
+
+This tutorial begins where :doc:`Tutorial 1 </intro/tutorial01>` left off. We're
+continuing the Web-poll application and will focus on Django's
+automatically-generated admin site.
+
+.. admonition:: Philosophy
+
+ Generating admin sites for your staff or clients to add, change and delete
+ content is tedious work that doesn't require much creativity. For that
+ reason, Django entirely automates creation of admin interfaces for models.
+
+ Django was written in a newsroom environment, with a very clear separation
+ between "content publishers" and the "public" site. Site managers use the
+ system to add news stories, events, sports scores, etc., and that content is
+ displayed on the public site. Django solves the problem of creating a
+ unified interface for site administrators to edit content.
+
+ The admin isn't necessarily intended to be used by site visitors; it's for
+ site managers.
+
+Activate the admin site
+=======================
+
+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 :setting:`INSTALLED_APPS` setting.
+
+ * Run ``python manage.py syncdb``. Since you have added a new application
+ to :setting:`INSTALLED_APPS`, the database tables need to be updated.
+
+ * Edit your ``mysite/urls.py`` file and uncomment the lines that reference
+ the admin -- there are three lines in total to uncomment. 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. In the end, you
+ should have a ``urls.py`` file that looks like this:
+
+ .. versionchanged:: 1.1
+ The method for adding admin urls has changed in Django 1.1.
+
+ .. parsed-literal::
+
+ from django.conf.urls.defaults import *
+
+ # Uncomment the next two lines to enable the admin:
+ **from django.contrib import admin**
+ **admin.autodiscover()**
+
+ urlpatterns = patterns('',
+ # Example:
+ # (r'^mysite/', include('mysite.foo.urls')),
+
+ # Uncomment the admin/doc line below and add 'django.contrib.admindocs'
+ # to INSTALLED_APPS to enable admin documentation:
+ # (r'^admin/doc/', include('django.contrib.admindocs.urls')),
+
+ # Uncomment the next line to enable the admin:
+ **(r'^admin/', include(admin.site.urls)),**
+ )
+
+ (The bold lines are the ones that needed to be uncommented.)
+
+Start the development server
+============================
+
+Let's start the development server and explore the admin site.
+
+Recall from Tutorial 1 that you start the development server like so:
+
+.. code-block:: bash
+
+ python manage.py runserver
+
+Now, open a Web browser and go to "/admin/" on your local domain -- e.g.,
+http://127.0.0.1:8000/admin/. You should see the admin's login screen:
+
+.. image:: _images/admin01.png
+ :alt: Django admin login screen
+
+Enter the admin site
+====================
+
+Now, try logging in. (You created a superuser account in the first part of this
+tutorial, remember? If you didn't create one or forgot the password you can
+:ref:`create another one <topics-auth-creating-superusers>`.) You should see
+the Django admin index page:
+
+.. image:: _images/admin02t.png
+ :alt: Django admin index page
+
+You should see a few other types of editable content, including groups, users
+and sites. These are core features Django ships with by default.
+
+Make the poll app modifiable in the admin
+=========================================
+
+But where's our poll app? It's not displayed on the admin index page.
+
+Just one thing to do: We need to tell the admin that ``Poll``
+objects have an admin interface. To do this, create a file called
+``admin.py`` in your ``polls`` directory, and edit it to look like this::
+
+ from polls.models import Poll
+ from django.contrib import admin
+
+ admin.site.register(Poll)
+
+You'll need to restart the development server to see your changes. Normally,
+the server auto-reloads code every time you modify a file, but the action of
+creating a new file doesn't trigger the auto-reloading logic.
+
+Explore the free admin functionality
+====================================
+
+Now that we've registered ``Poll``, Django knows that it should be displayed on
+the admin index page:
+
+.. image:: _images/admin03t.png
+ :alt: Django admin index page, now with polls displayed
+
+Click "Polls." Now you're at the "change list" page for polls. This page
+displays all the polls in the database and lets you choose one to change it.
+There's the "What's up?" poll we created in the first tutorial:
+
+.. image:: _images/admin04t.png
+ :alt: Polls change list page
+
+Click the "What's up?" poll to edit it:
+
+.. image:: _images/admin05t.png
+ :alt: Editing form for poll object
+
+Things to note here:
+
+ * The form is automatically generated from the Poll model.
+
+ * The different model field types (:class:`~django.db.models.DateTimeField`,
+ :class:`~django.db.models.CharField`) correspond to the appropriate HTML
+ input widget. Each type of field knows how to display itself in the Django
+ admin.
+
+ * Each :class:`~django.db.models.DateTimeField` gets free JavaScript
+ shortcuts. Dates get a "Today" shortcut and calendar popup, and times get
+ a "Now" shortcut and a convenient popup that lists commonly entered times.
+
+The bottom part of the page gives you a couple of options:
+
+ * Save -- Saves changes and returns to the change-list page for this type of
+ object.
+
+ * Save and continue editing -- Saves changes and reloads the admin page for
+ this object.
+
+ * Save and add another -- Saves changes and loads a new, blank form for this
+ type of object.
+
+ * Delete -- Displays a delete confirmation page.
+
+Change the "Date published" by clicking the "Today" and "Now" shortcuts. Then
+click "Save and continue editing." Then click "History" in the upper right.
+You'll see a page listing all changes made to this object via the Django admin,
+with the timestamp and username of the person who made the change:
+
+.. image:: _images/admin06t.png
+ :alt: History page for poll object
+
+Customize the admin form
+========================
+
+Take a few minutes to marvel at all the code you didn't have to write. By
+registering the Poll model with ``admin.site.register(Poll)``, Django was able
+to construct a default form representation. Often, you'll want to customize how
+the admin form looks and works. You'll do this by telling Django the options
+you want when you register the object.
+
+Let's see how this works by re-ordering the fields on the edit form. Replace
+the ``admin.site.register(Poll)`` line with::
+
+ class PollAdmin(admin.ModelAdmin):
+ fields = ['pub_date', 'question']
+
+ admin.site.register(Poll, PollAdmin)
+
+You'll follow this pattern -- create a model admin object, then pass it as the
+second argument to ``admin.site.register()`` -- any time you need to change the
+admin options for an object.
+
+This particular change above makes the "Publication date" come before the
+"Question" field:
+
+.. image:: _images/admin07.png
+ :alt: Fields have been reordered
+
+This isn't impressive with only two fields, but for admin forms with dozens
+of fields, choosing an intuitive order is an important usability detail.
+
+And speaking of forms with dozens of fields, you might want to split the form
+up into fieldsets::
+
+ class PollAdmin(admin.ModelAdmin):
+ fieldsets = [
+ (None, {'fields': ['question']}),
+ ('Date information', {'fields': ['pub_date']}),
+ ]
+
+ admin.site.register(Poll, PollAdmin)
+
+The first element of each tuple in ``fieldsets`` is the title of the fieldset.
+Here's what our form looks like now:
+
+.. image:: _images/admin08t.png
+ :alt: Form has fieldsets now
+
+You can assign arbitrary HTML classes to each fieldset. Django provides a
+``"collapse"`` class that displays a particular fieldset initially collapsed.
+This is useful when you have a long form that contains a number of fields that
+aren't commonly used::
+
+ class PollAdmin(admin.ModelAdmin):
+ fieldsets = [
+ (None, {'fields': ['question']}),
+ ('Date information', {'fields': ['pub_date'], 'classes': ['collapse']}),
+ ]
+
+.. image:: _images/admin09.png
+ :alt: Fieldset is initially collapsed
+
+Adding related objects
+======================
+
+OK, we have our Poll admin page. But a ``Poll`` has multiple ``Choices``, and
+the admin page doesn't display choices.
+
+Yet.
+
+There are two ways to solve this problem. The first is to register ``Choice``
+with the admin just as we did with ``Poll``. That's easy::
+
+ from polls.models import Choice
+
+ admin.site.register(Choice)
+
+Now "Choices" is an available option in the Django admin. The "Add choice" form
+looks like this:
+
+.. image:: _images/admin10.png
+ :alt: Choice admin page
+
+In that form, the "Poll" field is a select box containing every poll in the
+database. Django knows that a :class:`~django.db.models.ForeignKey` should be
+represented in the admin as a ``<select>`` box. In our case, only one poll
+exists at this point.
+
+Also note the "Add Another" link next to "Poll." Every object with a
+``ForeignKey`` relationship to another gets this for free. When you click "Add
+Another," you'll get a popup window with the "Add poll" form. If you add a poll
+in that window and click "Save," Django will save the poll to the database and
+dynamically add it as the selected choice on the "Add choice" form you're
+looking at.
+
+But, really, this is an inefficient way of adding Choice objects to the system.
+It'd be better if you could add a bunch of Choices directly when you create the
+Poll object. Let's make that happen.
+
+Remove the ``register()`` call for the Choice model. Then, edit the ``Poll``
+registration code to read::
+
+ class ChoiceInline(admin.StackedInline):
+ model = Choice
+ extra = 3
+
+ class PollAdmin(admin.ModelAdmin):
+ fieldsets = [
+ (None, {'fields': ['question']}),
+ ('Date information', {'fields': ['pub_date'], 'classes': ['collapse']}),
+ ]
+ inlines = [ChoiceInline]
+
+ admin.site.register(Poll, PollAdmin)
+
+This tells Django: "Choice objects are edited on the Poll admin page. By
+default, provide enough fields for 3 choices."
+
+Load the "Add poll" page to see how that looks, you may need to restart your development server:
+
+.. image:: _images/admin11t.png
+ :alt: Add poll page now has choices on it
+
+It works like this: There are three slots for related Choices -- as specified
+by ``extra`` -- and each time you come back to the "Change" page for an
+already-created object, you get another three extra slots.
+
+One small problem, though. It takes a lot of screen space to display all the
+fields for entering related Choice objects. For that reason, Django offers a
+tabular way of displaying inline related objects; you just need to change
+the ``ChoiceInline`` declaration to read::
+
+ class ChoiceInline(admin.TabularInline):
+ #...
+
+With that ``TabularInline`` (instead of ``StackedInline``), the
+related objects are displayed in a more compact, table-based format:
+
+.. image:: _images/admin12.png
+ :alt: Add poll page now has more compact choices
+
+Customize the admin change list
+===============================
+
+Now that the Poll admin page is looking good, let's make some tweaks to the
+"change list" page -- the one that displays all the polls in the system.
+
+Here's what it looks like at this point:
+
+.. image:: _images/admin04t.png
+ :alt: Polls change list page
+
+By default, Django displays the ``str()`` of each object. But sometimes it'd be
+more helpful if we could display individual fields. To do that, use the
+``list_display`` admin option, which is a tuple of field names to display, as
+columns, on the change list page for the object::
+
+ class PollAdmin(admin.ModelAdmin):
+ # ...
+ list_display = ('question', 'pub_date')
+
+Just for good measure, let's also include the ``was_published_today`` custom
+method from Tutorial 1::
+
+ class PollAdmin(admin.ModelAdmin):
+ # ...
+ list_display = ('question', 'pub_date', 'was_published_today')
+
+Now the poll change list page looks like this:
+
+.. image:: _images/admin13t.png
+ :alt: Polls change list page, updated
+
+You can click on the column headers to sort by those values -- except in the
+case of the ``was_published_today`` header, because sorting by the output of
+an arbitrary method is not supported. Also note that the column header for
+``was_published_today`` is, by default, the name of the method (with
+underscores replaced with spaces). But you can change that by giving that
+method (in ``models.py``) a ``short_description`` attribute::
+
+ def was_published_today(self):
+ return self.pub_date.date() == datetime.date.today()
+ was_published_today.short_description = 'Published today?'
+
+Edit your admin.py file again and add an improvement to the Poll change list page: Filters. Add the
+following line to ``PollAdmin``::
+
+ list_filter = ['pub_date']
+
+That adds a "Filter" sidebar that lets people filter the change list by the
+``pub_date`` field:
+
+.. image:: _images/admin14t.png
+ :alt: Polls change list page, updated
+
+The type of filter displayed depends on the type of field you're filtering on.
+Because ``pub_date`` is a DateTimeField, Django knows to give the default
+filter options for DateTimeFields: "Any date," "Today," "Past 7 days,"
+"This month," "This year."
+
+This is shaping up well. Let's add some search capability::
+
+ search_fields = ['question']
+
+That adds a search box at the top of the change list. When somebody enters
+search terms, Django will search the ``question`` field. You can use as many
+fields as you'd like -- although because it uses a ``LIKE`` query behind the
+scenes, keep it reasonable, to keep your database happy.
+
+Finally, because Poll objects have dates, it'd be convenient to be able to
+drill down by date. Add this line::
+
+ date_hierarchy = 'pub_date'
+
+That adds hierarchical navigation, by date, to the top of the change list page.
+At top level, it displays all available years. Then it drills down to months
+and, ultimately, days.
+
+Now's also a good time to note that change lists give you free pagination. The
+default is to display 50 items per page. Change-list pagination, search boxes,
+filters, date-hierarchies and column-header-ordering all work together like you
+think they should.
+
+Customize the admin look and feel
+=================================
+
+Clearly, having "Django administration" at the top of each admin page is
+ridiculous. It's just placeholder text.
+
+That's easy to change, though, using Django's template system. The Django admin
+is powered by Django itself, and its interfaces use Django's own template
+system.
+
+Open your settings file (``mysite/settings.py``, remember) and look at the
+:setting:`TEMPLATE_DIRS` setting. :setting:`TEMPLATE_DIRS` is a tuple of
+filesystem directories to check when loading Django templates. It's a search
+path.
+
+By default, :setting:`TEMPLATE_DIRS` is empty. So, let's add a line to it, to
+tell Django where our templates live::
+
+ TEMPLATE_DIRS = (
+ "/home/my_username/mytemplates", # Change this to your own directory.
+ )
+
+Now copy the template ``admin/base_site.html`` from within the default Django
+admin template directory in the source code of Django itself
+(``django/contrib/admin/templates``) into an ``admin`` subdirectory of
+whichever directory you're using in :setting:`TEMPLATE_DIRS`. For example, if
+your :setting:`TEMPLATE_DIRS` includes ``"/home/my_username/mytemplates"``, as
+above, then copy ``django/contrib/admin/templates/admin/base_site.html`` to
+``/home/my_username/mytemplates/admin/base_site.html``. Don't forget that
+``admin`` subdirectory.
+
+Then, just edit the file and replace the generic Django text with your own
+site's name as you see fit.
+
+This template file contains lots of text like ``{% block branding %}``
+and ``{{ title }}``. The ``{%`` and ``{{`` tags are part of Django's
+template language. When Django renders ``admin/base_site.html``, this
+template language will be evaluated to produce the final HTML page.
+Don't worry if you can't make any sense of the template right now --
+we'll delve into Django's templating language in Tutorial 3.
+
+Note that any of Django's default admin templates can be overridden. To
+override a template, just do the same thing you did with ``base_site.html`` --
+copy it from the default directory into your custom directory, and make
+changes.
+
+Astute readers will ask: But if :setting:`TEMPLATE_DIRS` was empty by default,
+how was Django finding the default admin templates? The answer is that, by
+default, Django automatically looks for a ``templates/`` subdirectory within
+each app package, for use as a fallback. See the :ref:`template loader
+documentation <template-loaders>` for full information.
+
+Customize the admin index page
+==============================
+
+On a similar note, you might want to customize the look and feel of the Django
+admin index page.
+
+By default, it displays all the apps in :setting:`INSTALLED_APPS` that have been
+registered with the admin application, in alphabetical order. You may want to
+make significant changes to the layout. After all, the index is probably the
+most important page of the admin, and it should be easy to use.
+
+The template to customize is ``admin/index.html``. (Do the same as with
+``admin/base_site.html`` in the previous section -- copy it from the default
+directory to your custom template directory.) Edit the file, and you'll see it
+uses a template variable called ``app_list``. That variable contains every
+installed Django app. Instead of using that, you can hard-code links to
+object-specific admin pages in whatever way you think is best. Again,
+don't worry if you can't understand the template language -- we'll cover that
+in more detail in Tutorial 3.
+
+When you're comfortable with the admin site, read :doc:`part 3 of this tutorial
+</intro/tutorial03>` to start working on public poll views.
diff --git a/parts/django/docs/intro/tutorial03.txt b/parts/django/docs/intro/tutorial03.txt
new file mode 100644
index 0000000..0843d9e
--- /dev/null
+++ b/parts/django/docs/intro/tutorial03.txt
@@ -0,0 +1,546 @@
+=====================================
+Writing your first Django app, part 3
+=====================================
+
+This tutorial begins where :doc:`Tutorial 2 </intro/tutorial02>` left off. We're
+continuing the Web-poll application and will focus on creating the public
+interface -- "views."
+
+Philosophy
+==========
+
+A view is a "type" of Web page in your Django application that generally serves
+a specific function and has a specific template. For example, in a Weblog
+application, you might have the following views:
+
+ * Blog homepage -- displays the latest few entries.
+
+ * Entry "detail" page -- permalink page for a single entry.
+
+ * Year-based archive page -- displays all months with entries in the
+ given year.
+
+ * Month-based archive page -- displays all days with entries in the
+ given month.
+
+ * Day-based archive page -- displays all entries in the given day.
+
+ * Comment action -- handles posting comments to a given entry.
+
+In our poll application, we'll have the following four views:
+
+ * Poll "archive" page -- displays the latest few polls.
+
+ * Poll "detail" page -- displays a poll question, with no results but
+ with a form to vote.
+
+ * Poll "results" page -- displays results for a particular poll.
+
+ * Vote action -- handles voting for a particular choice in a particular
+ poll.
+
+In Django, each view is represented by a simple Python function.
+
+Design your URLs
+================
+
+The first step of writing views is to design your URL structure. You do this by
+creating a Python module, called a URLconf. URLconfs are how Django associates
+a given URL with given Python code.
+
+When a user requests a Django-powered page, the system looks at the
+:setting:`ROOT_URLCONF` setting, which contains a string in Python dotted
+syntax. Django loads that module and looks for a module-level variable called
+``urlpatterns``, which is a sequence of tuples in the following format::
+
+ (regular expression, Python callback function [, optional dictionary])
+
+Django starts at the first regular expression and makes its way down the list,
+comparing the requested URL against each regular expression until it finds one
+that matches.
+
+When it finds a match, Django calls the Python callback function, with an
+:class:`~django.http.HttpRequest` object as the first argument, any "captured"
+values from the regular expression as keyword arguments, and, optionally,
+arbitrary keyword arguments from the dictionary (an optional third item in the
+tuple).
+
+For more on :class:`~django.http.HttpRequest` objects, see the
+:doc:`/ref/request-response`. For more details on URLconfs, see the
+:doc:`/topics/http/urls`.
+
+When you ran ``django-admin.py startproject mysite`` at the beginning of
+Tutorial 1, it created a default URLconf in ``mysite/urls.py``. It also
+automatically set your :setting:`ROOT_URLCONF` setting (in ``settings.py``) to
+point at that file::
+
+ ROOT_URLCONF = 'mysite.urls'
+
+Time for an example. Edit ``mysite/urls.py`` so it looks like this::
+
+ from django.conf.urls.defaults import *
+
+ from django.contrib import admin
+ admin.autodiscover()
+
+ urlpatterns = patterns('',
+ (r'^polls/$', 'polls.views.index'),
+ (r'^polls/(?P<poll_id>\d+)/$', 'polls.views.detail'),
+ (r'^polls/(?P<poll_id>\d+)/results/$', 'polls.views.results'),
+ (r'^polls/(?P<poll_id>\d+)/vote/$', 'polls.views.vote'),
+ (r'^admin/', include(admin.site.urls)),
+ )
+
+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 :setting:`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/(?P<poll_id>\d+)/$'`` -- it loads the
+function ``detail()`` from ``polls/views.py``. Finally, it calls that
+``detail()`` function like so::
+
+ detail(request=<HttpRequest object>, poll_id='23')
+
+The ``poll_id='23'`` part comes from ``(?P<poll_id>\d+)``. Using parentheses
+around a pattern "captures" the text matched by that pattern and sends it as an
+argument to the view function; the ``?P<poll_id>`` defines the name that will be
+used to identify the matched pattern; and ``\d+`` is a regular expression 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 ``.php``
+-- unless you have a sick sense of humor, in which case you can do something
+like this::
+
+ (r'^polls/latest\.php$', 'polls.views.index'),
+
+But, don't do that. It's silly.
+
+Note that these regular expressions do not search GET and POST parameters, or
+the domain name. For example, in a request to ``http://www.example.com/myapp/``,
+the URLconf will look for ``myapp/``. In a request to
+``http://www.example.com/myapp/?page=3``, the URLconf will look for ``myapp/``.
+
+If you need help with regular expressions, see `Wikipedia's entry`_ and the
+`Python documentation`_. Also, the O'Reilly book "Mastering Regular Expressions"
+by Jeffrey Friedl is fantastic.
+
+Finally, a performance note: these regular expressions are compiled the first
+time the URLconf module is loaded. They're super fast.
+
+.. _Wikipedia's entry: http://en.wikipedia.org/wiki/Regular_expression
+.. _Python documentation: http://docs.python.org/library/re.html
+
+Write your first view
+=====================
+
+Well, we haven't created any views yet -- we just have the URLconf. But let's
+make sure Django is following the URLconf properly.
+
+Fire up the Django development Web server:
+
+.. code-block:: bash
+
+ python manage.py runserver
+
+Now go to "http://localhost:8000/polls/" on your domain in your Web browser.
+You should get a pleasantly-colored error page with the following message::
+
+ ViewDoesNotExist at /polls/
+
+ Tried index in module polls.views. Error was: 'module'
+ object has no attribute 'index'
+
+This error happened because you haven't written a function ``index()`` in the
+module ``polls/views.py``.
+
+Try "/polls/23/", "/polls/23/results/" and "/polls/23/vote/". The error
+messages tell you which view Django tried (and failed to find, because you
+haven't written any views yet).
+
+Time to write the first view. Open the file ``polls/views.py``
+and put the following Python code in it::
+
+ from django.http import HttpResponse
+
+ def index(request):
+ return HttpResponse("Hello, world. You're at the poll index.")
+
+This is the simplest view possible. Go to "/polls/" in your browser, and you
+should see your text.
+
+Now lets add a few more views. These views are slightly different, because
+they take an argument (which, remember, is passed in from whatever was
+captured by the regular expression in the URLconf)::
+
+ def detail(request, poll_id):
+ return HttpResponse("You're looking at poll %s." % poll_id)
+
+ def results(request, poll_id):
+ return HttpResponse("You're looking at the results of poll %s." % poll_id)
+
+ def vote(request, poll_id):
+ return HttpResponse("You're voting on poll %s." % poll_id)
+
+Take a look in your browser, at "/polls/34/". It'll run the `detail()` method
+and display whatever ID you provide in the URL. Try "/polls/34/results/" and
+"/polls/34/vote/" too -- these will display the placeholder results and voting
+pages.
+
+Write views that actually do something
+======================================
+
+Each view is responsible for doing one of two things: Returning an
+:class:`~django.http.HttpResponse` object containing the content for the
+requested page, or raising an exception such as :exc:`~django.http.Http404`. The
+rest is up to you.
+
+Your view can read records from a database, or not. It can use a template
+system such as Django's -- or a third-party Python template system -- or not.
+It can generate a PDF file, output XML, create a ZIP file on the fly, anything
+you want, using whatever Python libraries you want.
+
+All Django wants is that :class:`~django.http.HttpResponse`. Or an exception.
+
+Because it's convenient, let's use Django's own database API, which we covered
+in :doc:`Tutorial 1 </intro/tutorial01>`. 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 polls.models import Poll
+ from django.http import HttpResponse
+
+ def index(request):
+ latest_poll_list = Poll.objects.all().order_by('-pub_date')[:5]
+ 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 polls.models import Poll
+ from django.http import HttpResponse
+
+ def index(request):
+ latest_poll_list = Poll.objects.all().order_by('-pub_date')[:5]
+ t = loader.get_template('polls/index.html')
+ c = Context({
+ 'latest_poll_list': latest_poll_list,
+ })
+ return HttpResponse(t.render(c))
+
+That code loads the template called "polls/index.html" and passes it a context.
+The context is a dictionary mapping template variable names to Python objects.
+
+Reload the page. Now you'll see an error::
+
+ TemplateDoesNotExist at /polls/
+ polls/index.html
+
+Ah. There's no template yet. First, create a directory, somewhere on your
+filesystem, whose contents Django can access. (Django runs as whatever user your
+server runs.) Don't put them under your document root, though. You probably
+shouldn't make them public, just for security's sake.
+Then edit :setting:`TEMPLATE_DIRS` in your ``settings.py`` to tell Django where
+it can find templates -- just as you did in the "Customize the admin look and
+feel" section of Tutorial 2.
+
+When you've done that, create a directory ``polls`` in your template directory.
+Within that, create a file called ``index.html``. Note that our
+``loader.get_template('polls/index.html')`` code from above maps to
+"[template_directory]/polls/index.html" on the filesystem.
+
+Put the following code in that template:
+
+.. code-block:: html+django
+
+ {% if latest_poll_list %}
+ <ul>
+ {% for poll in latest_poll_list %}
+ <li><a href="/polls/{{ poll.id }}/">{{ poll.question }}</a></li>
+ {% endfor %}
+ </ul>
+ {% else %}
+ <p>No polls are available.</p>
+ {% endif %}
+
+Load the page in your Web browser, and you should see a bulleted-list
+containing the "What's up" poll from Tutorial 1. The link points to the poll's
+detail page.
+
+A shortcut: render_to_response()
+--------------------------------
+
+It's a very common idiom to load a template, fill a context and return an
+:class:`~django.http.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 polls.models import Poll
+
+ def index(request):
+ latest_poll_list = Poll.objects.all().order_by('-pub_date')[:5]
+ return render_to_response('polls/index.html', {'latest_poll_list': latest_poll_list})
+
+Note that once we've done this in all these views, we no longer need to import
+:mod:`~django.template.loader`, :class:`~django.template.Context` and
+:class:`~django.http.HttpResponse`.
+
+The :func:`~django.shortcuts.render_to_response` function takes a template name
+as its first argument and a dictionary as its optional second argument. It
+returns an :class:`~django.http.HttpResponse` object of the given template
+rendered with the given context.
+
+Raising 404
+===========
+
+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 = Poll.objects.get(pk=poll_id)
+ except Poll.DoesNotExist:
+ raise Http404
+ return render_to_response('polls/detail.html', {'poll': p})
+
+The new concept here: The view raises the :exc:`~django.http.Http404` exception
+if a poll with the requested ID doesn't exist.
+
+We'll discuss what you could put in that ``polls/detail.html`` template a bit
+later, but if you'd like to quickly get the above example working, just::
+
+ {{ poll }}
+
+will get you started for now.
+
+A shortcut: get_object_or_404()
+-------------------------------
+
+It's a very common idiom to use :meth:`~django.db.models.QuerySet.get` and raise
+:exc:`~django.http.Http404` if the object doesn't exist. Django provides a
+shortcut. Here's the ``detail()`` view, rewritten::
+
+ from django.shortcuts import render_to_response, get_object_or_404
+ # ...
+ def detail(request, poll_id):
+ p = get_object_or_404(Poll, pk=poll_id)
+ return render_to_response('polls/detail.html', {'poll': p})
+
+The :func:`~django.shortcuts.get_object_or_404` function takes a Django model
+as its first argument and an arbitrary number of keyword arguments, which it
+passes to the module's :meth:`~django.db.models.QuerySet.get` function. It
+raises :exc:`~django.http.Http404` if the object doesn't exist.
+
+.. admonition:: Philosophy
+
+ Why do we use a helper function :func:`~django.shortcuts.get_object_or_404`
+ instead of automatically catching the
+ :exc:`~django.core.exceptions.ObjectDoesNotExist` exceptions at a higher
+ level, or having the model API raise :exc:`~django.http.Http404` instead of
+ :exc:`~django.core.exceptions.ObjectDoesNotExist`?
+
+ 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 :func:`~django.shortcuts.get_list_or_404` function, which works
+just as :func:`~django.shortcuts.get_object_or_404` -- except using
+:meth:`~django.db.models.QuerySet.filter` instead of
+:meth:`~django.db.models.QuerySet.get`. It raises :exc:`~django.http.Http404` if
+the list is empty.
+
+Write a 404 (page not found) view
+=================================
+
+When you raise :exc:`~django.http.Http404` from within a view, Django will load
+a special view devoted to handling 404 errors. It finds it by looking for the
+variable ``handler404``, which is a string in Python dotted syntax -- the same
+format the normal URLconf callbacks use. A 404 view itself has nothing special:
+It's just a normal view.
+
+You normally won't have to bother with writing 404 views. By default, URLconfs
+have the following line up top::
+
+ from django.conf.urls.defaults import *
+
+That takes care of setting ``handler404`` in the current module. As you can see
+in ``django/conf/urls/defaults.py``, ``handler404`` is set to
+:func:`django.views.defaults.page_not_found` by default.
+
+Four more things to note about 404 views:
+
+ * If :setting:`DEBUG` is set to ``True`` (in your settings module) then your
+ 404 view will never be used (and thus the ``404.html`` template will never
+ be rendered) because the traceback will be displayed instead.
+
+ * The 404 view is also called if Django doesn't find a match after checking
+ every regular expression in the URLconf.
+
+ * If you don't define your own 404 view -- and simply use the default, which
+ is recommended -- you still have one obligation: To create a ``404.html``
+ template in the root of your template directory. The default 404 view will
+ use that template for all 404 errors.
+
+ * If :setting:`DEBUG` is set to ``False`` (in your settings module) and if
+ you didn't create a ``404.html`` file, an ``Http500`` is raised instead.
+ So remember to create a ``404.html``.
+
+Write a 500 (server error) view
+===============================
+
+Similarly, URLconfs may define a ``handler500``, which points to a view to call
+in case of server errors. Server errors happen when you have runtime errors in
+view code.
+
+Use the template system
+=======================
+
+Back to the ``detail()`` view for our poll application. Given the context
+variable ``poll``, here's what the "polls/detail.html" template might look
+like:
+
+.. code-block:: html+django
+
+ <h1>{{ poll.question }}</h1>
+ <ul>
+ {% for choice in poll.choice_set.all %}
+ <li>{{ choice.choice }}</li>
+ {% endfor %}
+ </ul>
+
+The template system uses dot-lookup syntax to access variable attributes. In
+the example of ``{{ poll.question }}``, first Django does a dictionary lookup
+on the object ``poll``. Failing that, it tries attribute lookup -- which works,
+in this case. If attribute lookup had failed, it would've tried calling the
+method ``question()`` on the poll object.
+
+Method-calling happens in the ``{% for %}`` loop: ``poll.choice_set.all`` is
+interpreted as the Python code ``poll.choice_set.all()``, which returns an
+iterable of Choice objects and is suitable for use in the ``{% for %}`` tag.
+
+See the :doc:`template guide </topics/templates>` for more about templates.
+
+Simplifying the URLconfs
+========================
+
+Take some time to play around with the views and template system. As you edit
+the URLconf, you may notice there's a fair bit of redundancy in it::
+
+ urlpatterns = patterns('',
+ (r'^polls/$', 'polls.views.index'),
+ (r'^polls/(?P<poll_id>\d+)/$', 'polls.views.detail'),
+ (r'^polls/(?P<poll_id>\d+)/results/$', 'polls.views.results'),
+ (r'^polls/(?P<poll_id>\d+)/vote/$', 'polls.views.vote'),
+ )
+
+Namely, ``polls.views`` is in every callback.
+
+Because this is a common case, the URLconf framework provides a shortcut for
+common prefixes. You can factor out the common prefixes and add them as the
+first argument to :func:`~django.conf.urls.defaults.patterns`, like so::
+
+ urlpatterns = patterns('polls.views',
+ (r'^polls/$', 'index'),
+ (r'^polls/(?P<poll_id>\d+)/$', 'detail'),
+ (r'^polls/(?P<poll_id>\d+)/results/$', 'results'),
+ (r'^polls/(?P<poll_id>\d+)/vote/$', 'vote'),
+ )
+
+This is functionally identical to the previous formatting. It's just a bit
+tidier.
+
+Since you generally don't want the prefix for one app to be applied to every
+callback in your URLconf, you can concatenate multiple
+:func:`~django.conf.urls.defaults.patterns`. Your full ``mysite/urls.py`` might
+now look like this::
+
+ from django.conf.urls.defaults import *
+
+ from django.contrib import admin
+ admin.autodiscover()
+
+ urlpatterns = patterns('polls.views',
+ (r'^polls/$', 'index'),
+ (r'^polls/(?P<poll_id>\d+)/$', 'detail'),
+ (r'^polls/(?P<poll_id>\d+)/results/$', 'results'),
+ (r'^polls/(?P<poll_id>\d+)/vote/$', 'vote'),
+ )
+
+ urlpatterns += patterns('',
+ (r'^admin/', include(admin.site.urls)),
+ )
+
+Decoupling the URLconfs
+=======================
+
+While we're at it, we should take the time to decouple our poll-app URLs from
+our Django project configuration. Django apps are meant to be pluggable -- that
+is, each particular app should be transferable to another Django installation
+with minimal fuss.
+
+Our poll app is pretty decoupled at this point, thanks to the strict directory
+structure that ``python manage.py startapp`` created, but one part of it is
+coupled to the Django settings: The URLconf.
+
+We've been editing the URLs in ``mysite/urls.py``, but the URL design of an
+app is specific to the app, not to the Django installation -- so let's move the
+URLs within the app directory.
+
+Copy the file ``mysite/urls.py`` to ``polls/urls.py``. Then, change
+``mysite/urls.py`` to remove the poll-specific URLs and insert an
+:func:`~django.conf.urls.defaults.include`, leaving you with::
+
+ # This also imports the include function
+ from django.conf.urls.defaults import *
+
+ from django.contrib import admin
+ admin.autodiscover()
+
+ urlpatterns = patterns('',
+ (r'^polls/', include('polls.urls')),
+ (r'^admin/', include(admin.site.urls)),
+ )
+
+:func:`~django.conf.urls.defaults.include` simply references another URLconf.
+Note that the regular expression doesn't have a ``$`` (end-of-string match
+character) but has the trailing slash. Whenever Django encounters
+:func:`~django.conf.urls.defaults.include`, it chops off whatever part of the
+URL matched up to that point and sends the remaining string to the included
+URLconf for further processing.
+
+Here's what happens if a user goes to "/polls/34/" in this system:
+
+ * Django will find the match at ``'^polls/'``
+
+ * Then, Django will strip off the matching text (``"polls/"``) and send the
+ remaining text -- ``"34/"`` -- to the 'polls.urls' URLconf for
+ further processing.
+
+Now that we've decoupled that, we need to decouple the ``polls.urls``
+URLconf by removing the leading "polls/" from each line, and removing the
+lines registering the admin site. Your ``polls.urls`` file should now look like
+this::
+
+ from django.conf.urls.defaults import *
+
+ urlpatterns = patterns('polls.views',
+ (r'^$', 'index'),
+ (r'^(?P<poll_id>\d+)/$', 'detail'),
+ (r'^(?P<poll_id>\d+)/results/$', 'results'),
+ (r'^(?P<poll_id>\d+)/vote/$', 'vote'),
+ )
+
+The idea behind :func:`~django.conf.urls.defaults.include` and URLconf
+decoupling is to make it easy to plug-and-play URLs. Now that polls are in their
+own URLconf, they can be placed under "/polls/", or under "/fun_polls/", or
+under "/content/polls/", or any other path root, and the app will still work.
+
+All the poll app cares about is its relative path, not its absolute path.
+
+When you're comfortable with writing views, read :doc:`part 4 of this tutorial
+</intro/tutorial04>` to learn about simple form processing and generic views.
diff --git a/parts/django/docs/intro/tutorial04.txt b/parts/django/docs/intro/tutorial04.txt
new file mode 100644
index 0000000..dfbd82d
--- /dev/null
+++ b/parts/django/docs/intro/tutorial04.txt
@@ -0,0 +1,346 @@
+=====================================
+Writing your first Django app, part 4
+=====================================
+
+This tutorial begins where :doc:`Tutorial 3 </intro/tutorial03>` 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 ``<form>`` element:
+
+.. code-block:: html+django
+
+ <h1>{{ poll.question }}</h1>
+
+ {% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}
+
+ <form action="/polls/{{ poll.id }}/vote/" method="post">
+ {% csrf_token %}
+ {% for choice in poll.choice_set.all %}
+ <input type="radio" name="choice" id="choice{{ forloop.counter }}" value="{{ choice.id }}" />
+ <label for="choice{{ forloop.counter }}">{{ choice.choice }}</label><br />
+ {% endfor %}
+ <input type="submit" value="Vote" />
+ </form>
+
+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 :ttag:`for` tag has gone
+ through its loop
+
+ * Since we're creating a POST form (which can have the effect of modifying
+ data), we need to worry about Cross Site Request Forgeries.
+ Thankfully, you don't have to worry too hard, because Django comes with
+ a very easy-to-use system for protecting against it. In short, all POST
+ forms that are targeted at internal URLs should use the ``{% csrf_token %}``
+ template tag.
+
+The ``{% csrf_token %}`` tag requires information from the request object, which
+is not normally accessible from within the template context. To fix this, a
+small adjustment needs to be made to the ``detail`` view, so that it looks like
+the following::
+
+ from django.template import RequestContext
+ # ...
+ def detail(request, poll_id):
+ p = get_object_or_404(Poll, pk=poll_id)
+ return render_to_response('polls/detail.html', {'poll': p},
+ context_instance=RequestContext(request))
+
+The details of how this works are explained in the documentation for
+:ref:`RequestContext <subclassing-context-requestcontext>`.
+
+Now, let's create a Django view that handles the submitted data and does
+something with it. Remember, in :doc:`Tutorial 3 </intro/tutorial03>`, we
+created a URLconf for the polls application that includes this line::
+
+ (r'^(?P<poll_id>\d+)/vote/$', 'vote'),
+
+We also created a dummy implementation of the ``vote()`` function. Let's
+create a real version. Add the following to ``polls/views.py``::
+
+ from django.shortcuts import get_object_or_404, render_to_response
+ from django.http import HttpResponseRedirect, HttpResponse
+ from django.core.urlresolvers import reverse
+ from django.template import RequestContext
+ from 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.",
+ }, context_instance=RequestContext(request))
+ 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('polls.views.results', args=(p.id,)))
+
+This code includes a few things we haven't covered yet in this tutorial:
+
+ * :attr:`request.POST <django.http.HttpRequest.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. :attr:`request.POST <django.http.HttpRequest.POST>` values are
+ always strings.
+
+ Note that Django also provides :attr:`request.GET
+ <django.http.HttpRequest.GET>` for accessing GET data in the same way --
+ but we're explicitly using :attr:`request.POST
+ <django.http.HttpRequest.POST>` in our code, to ensure that data is only
+ altered via a POST call.
+
+ * ``request.POST['choice']`` will raise :exc:`KeyError` if ``choice`` wasn't
+ provided in POST data. The above code checks for :exc:`KeyError` and
+ redisplays the poll form with an error message if ``choice`` isn't given.
+
+ * After incrementing the choice count, the code returns an
+ :class:`~django.http.HttpResponseRedirect` rather than a normal
+ :class:`~django.http.HttpResponse`.
+ :class:`~django.http.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
+ :class:`~django.http.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 :func:`~django.core.urlresolvers.reverse` function in the
+ :class:`~django.http.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
+ :func:`~django.core.urlresolvers.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).
+
+As mentioned in Tutorial 3, ``request`` is a :class:`~django.http.HttpRequest`
+object. For more on :class:`~django.http.HttpRequest` objects, see the
+:doc:`request and response documentation </ref/request-response>`.
+
+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 :doc:`Tutorial 3
+</intro/tutorial03>`. The only difference is the template name. We'll fix this
+redundancy later.
+
+Now, create a ``results.html`` template:
+
+.. code-block:: html+django
+
+ <h1>{{ poll.question }}</h1>
+
+ <ul>
+ {% for choice in poll.choice_set.all %}
+ <li>{{ choice.choice }} -- {{ choice.votes }} vote{{ choice.votes|pluralize }}</li>
+ {% endfor %}
+ </ul>
+
+ <a href="/polls/{{ poll.id }}/">Vote again?</a>
+
+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.
+
+Use generic views: Less code is better
+======================================
+
+The ``detail()`` (from :doc:`Tutorial 3 </intro/tutorial03>`) 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. We will:
+
+ 1. Convert the URLconf.
+
+ 2. Rename a few templates.
+
+ 3. Delete some of the old, unneeded views.
+
+ 4. Fix up URL handling for the new views.
+
+Read on for details.
+
+.. 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('polls.views',
+ (r'^$', 'index'),
+ (r'^(?P<poll_id>\d+)/$', 'detail'),
+ (r'^(?P<poll_id>\d+)/results/$', 'results'),
+ (r'^(?P<poll_id>\d+)/vote/$', 'vote'),
+ )
+
+Change it like so::
+
+ from django.conf.urls.defaults import *
+ from polls.models import Poll
+
+ info_dict = {
+ 'queryset': Poll.objects.all(),
+ }
+
+ urlpatterns = patterns('',
+ (r'^$', 'django.views.generic.list_detail.object_list', info_dict),
+ (r'^(?P<object_id>\d+)/$', 'django.views.generic.list_detail.object_detail', info_dict),
+ url(r'^(?P<object_id>\d+)/results/$', 'django.views.generic.list_detail.object_detail', dict(info_dict, template_name='polls/results.html'), 'poll_results'),
+ (r'^(?P<poll_id>\d+)/vote/$', 'polls.views.vote'),
+ )
+
+We're using two generic views here:
+:func:`~django.views.generic.list_detail.object_list` and
+:func:`~django.views.generic.list_detail.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 :func:`~django.views.generic.list_detail.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
+ :ref:`naming URL patterns <naming-url-patterns>` for information). We're
+ also using the :func:`~django.conf.urls.default.url` function from
+ :mod:`django.conf.urls.defaults` here. It's a good habit to use
+ :func:`~django.conf.urls.defaults.url` when you are providing a pattern
+ name like this.
+
+By default, the :func:`~django.views.generic.list_detail.object_detail` generic
+view uses a template called ``<app name>/<model name>_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
+:func:`~django.shortcuts.render_to_response` line in ``vote()``.
+
+Similarly, the :func:`~django.views.generic.list_detail.object_list` generic
+view uses a template called ``<app name>/<model name>_list.html``. Thus, rename
+``polls/index.html`` to ``polls/poll_list.html``.
+
+Because we have more than one entry in the URLconf that uses
+:func:`~django.views.generic.list_detail.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:: :meth:`django.db.models.QuerySet.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
+ :class:`~django.db.models.QuerySet`, which is "lazy" and doesn't hit your
+ database until it absolutely has to. By the time the database query happens,
+ the :func:`~django.views.generic.list_detail.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 :ref:`explains the lazy nature of QuerySet objects
+ <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 :func:`~django.shortcuts.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
+:func:`~django.core.urlresolvers.reverse` function to avoid hard-coding our
+URLs. Now that we've switched to a generic view, we'll need to change the
+:func:`~django.core.urlresolvers.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 :doc:`generic views documentation
+</topics/http/generic-views>`.
+
+Coming soon
+===========
+
+The tutorial ends here for the time being. Future installments of the tutorial
+will cover:
+
+ * 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 might want to check out some pointers on :doc:`where to go
+from here </intro/whatsnext>`
diff --git a/parts/django/docs/intro/whatsnext.txt b/parts/django/docs/intro/whatsnext.txt
new file mode 100644
index 0000000..00c1654
--- /dev/null
+++ b/parts/django/docs/intro/whatsnext.txt
@@ -0,0 +1,231 @@
+=================
+What to read next
+=================
+
+So you've read all the :doc:`introductory material </intro/index>` and have
+decided you'd like to keep using Django. We've only just scratched the surface
+with this intro (in fact, if you've read every single word you've still read
+less than 10% of the overall documentation).
+
+So what's next?
+
+Well, we've always been big fans of learning by doing. At this point you should
+know enough to start a project of your own and start fooling around. As you need
+to learn new tricks, come back to the documentation.
+
+We've put a lot of effort into making Django's documentation useful, easy to
+read and as complete as possible. The rest of this document explains more about
+how the documentation works so that you can get the most out of it.
+
+(Yes, this is documentation about documentation. Rest assured we have no plans
+to write a document about how to read the document about documentation.)
+
+Finding documentation
+=====================
+
+Django's got a *lot* of documentation -- almost 200,000 words -- so finding what
+you need can sometimes be tricky. A few good places to start are the :ref:`search`
+and the :ref:`genindex`.
+
+Or you can just browse around!
+
+How the documentation is organized
+==================================
+
+Django's main documentation is broken up into "chunks" designed to fill
+different needs:
+
+ * The :doc:`introductory material </intro/index>` is designed for people new
+ to Django -- or to Web development in general. It doesn't cover anything
+ in depth, but instead gives a high-level overview of how developing in
+ Django "feels".
+
+ * The :doc:`topic guides </topics/index>`, on the other hand, dive deep into
+ individual parts of Django. There are complete guides to Django's
+ :doc:`model system </topics/db/index>`, :doc:`template engine
+ </topics/templates>`, :doc:`forms framework </topics/forms/index>`, and much
+ more.
+
+ This is probably where you'll want to spend most of your time; if you work
+ your way through these guides you should come out knowing pretty much
+ everything there is to know about Django.
+
+ * Web development is often broad, not deep -- problems span many domains.
+ We've written a set of :doc:`how-to guides </howto/index>` that answer
+ common "How do I ...?" questions. Here you'll find information about
+ :doc:`generating PDFs with Django </howto/outputting-pdf>`, :doc:`writing
+ custom template tags </howto/custom-template-tags>`, and more.
+
+ Answers to really common questions can also be found in the :doc:`FAQ
+ </faq/index>`.
+
+ * The guides and how-to's don't cover every single class, function, and
+ method available in Django -- that would be overwhelming when you're
+ trying to learn. Instead, details about individual classes, functions,
+ methods, and modules are kept in the :doc:`reference </ref/index>`. This is
+ where you'll turn to find the details of a particular function or
+ whathaveyou.
+
+ * Finally, there's some "specialized" documentation not usually relevant to
+ most developers. This includes the :doc:`release notes </releases/index>`,
+ :doc:`documentation of obsolete features </obsolete/index>`,
+ :doc:`internals documentation </internals/index>` for those who want to add
+ code to Django itself, and a :doc:`few other things that simply don't fit
+ elsewhere </misc/index>`.
+
+
+How documentation is updated
+============================
+
+Just as the Django code base is developed and improved on a daily basis, our
+documentation is consistently improving. We improve documentation for several
+reasons:
+
+ * To make content fixes, such as grammar/typo corrections.
+
+ * To add information and/or examples to existing sections that need to be
+ expanded.
+
+ * To document Django features that aren't yet documented. (The list of
+ such features is shrinking but exists nonetheless.)
+
+ * To add documentation for new features as new features get added, or as
+ Django APIs or behaviors change.
+
+Django's documentation is kept in the same source control system as its code. It
+lives in the `django/trunk/docs`_ directory of our Subversion repository. Each
+document online is a separate text file in the repository.
+
+.. _django/trunk/docs: http://code.djangoproject.com/browser/django/trunk/docs
+
+Where to get it
+===============
+
+You can read Django documentation in several ways. They are, in order of
+preference:
+
+On the Web
+----------
+
+The most recent version of the Django documentation lives at
+http://docs.djangoproject.com/en/dev/. These HTML pages are generated
+automatically from the text files in source control. That means they reflect the
+"latest and greatest" in Django -- they include the very latest corrections and
+additions, and they discuss the latest Django features, which may only be
+available to users of the Django development version. (See "Differences between
+versions" below.)
+
+We encourage you to help improve the docs by submitting changes, corrections and
+suggestions in the `ticket system`_. The Django developers actively monitor the
+ticket system and use your feedback to improve the documentation for everybody.
+
+Note, however, that tickets should explicitly relate to the documentation,
+rather than asking broad tech-support questions. If you need help with your
+particular Django setup, try the `django-users mailing list`_ or the `#django
+IRC channel`_ instead.
+
+.. _ticket system: http://code.djangoproject.com/simpleticket?component=Documentation
+.. _django-users mailing list: http://groups.google.com/group/django-users
+.. _#django IRC channel: irc://irc.freenode.net/django
+
+In plain text
+-------------
+
+For offline reading, or just for convenience, you can read the Django
+documentation in plain text.
+
+If you're using an official release of Django, note that the zipped package
+(tarball) of the code includes a ``docs/`` directory, which contains all the
+documentation for that release.
+
+If you're using the development version of Django (aka the Subversion "trunk"),
+note that the ``docs/`` directory contains all of the documentation. You can
+``svn update`` it, just as you ``svn update`` the Python code, in order to get
+the latest changes.
+
+You can check out the latest Django documentation from Subversion using this
+shell command:
+
+.. code-block:: bash
+
+ $ svn co http://code.djangoproject.com/svn/django/trunk/docs/ django_docs
+
+One low-tech way of taking advantage of the text documentation is by using the
+Unix ``grep`` utility to search for a phrase in all of the documentation. For
+example, this will show you each mention of the phrase "max_length" in any
+Django document:
+
+.. code-block:: bash
+
+ $ grep -r max_length /path/to/django/docs/
+
+As HTML, locally
+----------------
+
+You can get a local copy of the HTML documentation following a few easy steps:
+
+ * Django's documentation uses a system called Sphinx__ to convert from
+ plain text to HTML. You'll need to install Sphinx by either downloading
+ and installing the package from the Sphinx Web site, or by Python's
+ ``easy_install``:
+
+ .. code-block:: bash
+
+ $ easy_install Sphinx
+
+ * Then, just use the included ``Makefile`` to turn the documentation into
+ HTML:
+
+ .. code-block:: bash
+
+ $ cd path/to/django/docs
+ $ make html
+
+ You'll need `GNU Make`__ installed for this.
+
+ * The HTML documentation will be placed in ``docs/_build/html``.
+
+.. note::
+
+ Generation of the Django documentation will work with Sphinx version 0.6
+ or newer, but we recommend going straight to Sphinx 1.0.2 or newer.
+
+__ http://sphinx.pocoo.org/
+__ http://www.gnu.org/software/make/
+
+Differences between versions
+============================
+
+As previously mentioned, the text documentation in our Subversion repository
+contains the "latest and greatest" changes and additions. These changes often
+include documentation of new features added in the Django development version
+-- the Subversion ("trunk") version of Django. For that reason, it's worth
+pointing out our policy on keeping straight the documentation for various
+versions of the framework.
+
+We follow this policy:
+
+ * The primary documentation on djangoproject.com is an HTML version of the
+ latest docs in Subversion. These docs always correspond to the latest
+ official Django release, plus whatever features we've added/changed in
+ the framework *since* the latest release.
+
+ * As we add features to Django's development version, we try to update the
+ documentation in the same Subversion commit transaction.
+
+ * To distinguish feature changes/additions in the docs, we use the phrase:
+ "New in version X.Y", being X.Y the next release version (hence, the one
+ being developed).
+
+ * Documentation for a particular Django release is frozen once the version
+ has been released officially. It remains a snapshot of the docs as of the
+ moment of the release. We will make exceptions to this rule in
+ the case of retroactive security updates or other such retroactive
+ changes. Once documentation is frozen, we add a note to the top of each
+ frozen document that says "These docs are frozen for Django version XXX"
+ and links to the current version of that document.
+
+ * The `main documentation Web page`_ includes links to documentation for
+ all previous versions.
+
+.. _main documentation Web page: http://docs.djangoproject.com/en/dev/