1
0
mirror of https://github.com/django/django.git synced 2025-10-24 06:06:09 +00:00

Massive reorganization of the docs. See the new docs online at http://docs.djangoproject.com/.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@8506 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Jacob Kaplan-Moss
2008-08-23 22:25:40 +00:00
parent b3688e8194
commit 97cb07c3a1
188 changed files with 19913 additions and 17059 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

38
docs/intro/index.txt Normal file
View File

@@ -0,0 +1,38 @@
.. _intro-index:
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

75
docs/intro/install.txt Normal file
View File

@@ -0,0 +1,75 @@
.. _intro-install:
Quick install guide
===================
Before you can use Django, you'll need to get it installed. We have a
:ref:`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 2.3 and higher, 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.
You can verify that Python's installed py 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 :ref:`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.
.. warning::
If 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; if you
try to use them with an official release they won't work.
That's it!
----------
That's it -- you can now :ref:`move onto the tutorial <intro-tutorial01>`.

314
docs/intro/overview.txt Normal file
View File

@@ -0,0 +1,314 @@
.. _intro-overview:
==================
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
:ref:`start with the tutorial <intro-tutorial01>` or :ref:`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 :ref:`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::
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, :ref:`Python API <topics-db-queries>` to
access your data. The API is created on the fly, no code generation necessary::
>>> from mysite.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()
[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)
John Smith
>>> Reporter.objects.get(full_name__startswith='John')
John Smith
>>> Reporter.objects.get(full_name__contains='mith')
John Smith
>>> Reporter.objects.get(id=2)
Traceback (most recent call last):
...
DoesNotExist: Reporter does not exist for {'id__exact': 2}
# 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()
[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()
[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")
[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 :ref:`administrative interface <ref-contrib-admin>` -- a Web
site that lets authenticated users add, change and delete objects. It's as easy
as adding a line of code to your model classes::
class Article(models.Model):
pub_date = models.DateTimeField()
headline = models.CharField(max_length=200)
content = models.TextField()
reporter = models.ForeignKey(Reporter)
class Admin: pass
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 :ref:`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})/$', 'mysite.views.year_archive'),
(r'^articles/(\d{4})/(\d{2})/$', 'mysite.views.month_archive'),
(r'^articles/(\d{4})/(\d{2})/(\d+)/$', 'mysite.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 ``mysite.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 :ref:`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 :ref:`caching framework <topics-cache>` that integrates with memcached
or other backends.
* A :ref:`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 :ref:`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/

690
docs/intro/tutorial01.txt Normal file
View File

@@ -0,0 +1,690 @@
.. _intro-tutorial01:
=====================================
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 :ref:`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:: 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 :ref:`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).
:ref:`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 :ref:`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 :ref:`ref-django-admin`.
* :file:`settings.py`: Settings/configuration for this Django project.
:ref:`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
:ref:`topics-http-urls`.
.. _more about packages: http://docs.python.org/tut/node8.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 0.96, using settings 'mysite.settings'
Development server is running at http://127.0.0.1:8000/
Quit the server with CONTROL-C (Unix) or CTRL-BREAK (Windows).
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 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
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 these settings to match your
database's connection parameters:
* :setting:`DATABASE_ENGINE` -- Either 'postgresql_psycopg2', 'mysql' or
'sqlite3'. Other backends are :setting:`also available <DATABASE_ENGINE>`.
* :setting:`DATABASE_NAME` -- The name of your database. If you're using
SQLite, the database will be a file on your computer; in that case,
``DATABASE_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).
* :setting:`DATABASE_USER` -- Your database username (not used for SQLite).
* :setting:`DATABASE_PASSWORD` -- Your database password (not used for
SQLite).
* :setting:`DATABASE_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:`DATABASE_ENGINE` to ``'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.
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.
In this tutorial, we'll create our poll app in the :file:`mysite` directory,
for simplicity. As a consequence, the app will be coupled to the project --
that is, Python code within the poll app will refer to ``mysite.polls``.
Later in this tutorial, we'll discuss decoupling your apps for distribution.
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
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()
.. admonition:: Errors about :attr:`~django.db.models.Field.max_length`
If Django gives you an error message saying that
:attr:`~django.db.models.Field.max_length` is not a valid argument, you're
most likely using an old version of Django. (This version of the tutorial is
written for the latest development version of Django.) If you're using a
Subversion checkout of Django's development version (see :ref:`the
installation docs <topics-install>` for more information), you shouldn't have
any problems.
If you want to stick with an older version of Django, you'll want to switch
to `the Django 0.96 tutorial`_, because this tutorial covers several features
that only exist in the Django development version.
.. _the Django 0.96 tutorial: http://www.djangoproject.com/documentation/0.96/tutorial01/
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/tut/node8.html#SECTION008110000000000000000
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 ``'mysite.polls'``. So
it'll look like this::
INSTALLED_APPS = (
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
'mysite.polls'
)
Now Django knows ``mysite`` includes 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 :ref:`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 ``mysite`` on ``sys.path``. For flexibility, several pieces of
Django refer to projects in Python dotted-path notation (e.g.
``'mysite.polls.models'``). In order for this to work, the ``mysite``
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``
is at the root level on the Python path (i.e., ``import mysite`` works) and
set the ``DJANGO_SETTINGS_MODULE`` environment variable to
``mysite.settings``.
For more information on all of this, see the :ref:`django-admin.py
documentation <ref-django-admin>`.
Once you're in the shell, explore the :ref:`database API <topics-db-queries>`::
>>> from mysite.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
.. admonition:: If :meth:`~django.db.models.Model.__unicode__` doesn't seem to work
If you add the :meth:`~django.db.models.Model.__unicode__` method to your
models and don't see any change in how they're represented, you're most
likely using an old version of Django. (This version of the tutorial is
written for the latest development version of Django.) If you're using a
Subversion checkout of of Django's development version (see :ref:`the
installation docs <topics-install>` for more information), you shouldn't have
any problems.
If you want to stick with an older version of Django, you'll want to switch
to `the Django 0.96 tutorial`_, because this tutorial covers several features
that only exist in the Django development version.
.. _the Django 0.96 tutorial: http://www.djangoproject.com/documentation/0.96/tutorial01/
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.
Let's jump back into the Python interactive shell by running
``python manage.py shell`` again::
>>> from mysite.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. Of course, if you're going through this
# tutorial in another year, change as appropriate.
>>> 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.
>>> p = Poll.objects.get(pk=1)
>>> 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 full details on the database API, see our :ref:`Database API reference
<topics-db-queries>`.
When you're comfortable with the API, read :ref:`part 2 of this tutorial
<intro-tutorial02>` to get Django's automatic admin working.

457
docs/intro/tutorial02.txt Normal file
View File

@@ -0,0 +1,457 @@
.. _intro-tutorial02:
=====================================
Writing your first Django app, part 2
=====================================
This tutorial begins where :ref:`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 below the
"Uncomment this for admin:" comments. 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:
.. 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'^{{ project_name }}/', include('{{ project_name }}.foo.urls')),
# Uncomment the next line to enable admin documentation:
# (r'^admin/doc/', include('django.contrib.admindocs.urls')),
# Uncomment the next line for to enable the admin:
**(r'^admin/(.*)', admin.site.root),**
)
(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?) 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. Edit the ``mysite/polls/admin.py`` file and
add the following to the bottom of the file::
from mysite.polls.models import Poll
from django.contrib import admin
admin.site.register(Poll)
Now reload the Django admin page to see your changes. Note that you don't have
to restart the development server -- the server will auto-reload your project,
so any modifications code will be seen immediately in your browser.
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. When you
call ``admin.site.register(Poll)``, Django just lets you edit the object and
"guess" at how to display it within the admin. Often you'll want to control how
the admin looks and works. You'll do this by telling Django about the options
you want when you register the object.
Let's see how this works by reordering 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 register ``Choice`` with the
admin just as we did with ``Poll``. That's easy::
from mysite.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::
poll = models.ForeignKey(Poll, edit_inline=models.STACKED, num_in_admin=3)
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:
.. 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 a ``short_description`` attribute::
def was_published_today(self):
return self.pub_date.date() == datetime.date.today()
was_published_today.short_description = 'Published today?'
Let's add another 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. (How meta!)
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 (``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.
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.
Django offers another shortcut in this department. Run the command ``python
manage.py adminindex polls`` to get a chunk of template code for inclusion in
the admin index template. It's a useful starting point.
When you're comfortable with the admin site, read :ref:`part 3 of this tutorial
<intro-tutorial03>` to start working on public poll views.

490
docs/intro/tutorial03.txt Normal file
View File

@@ -0,0 +1,490 @@
.. _intro-tutorial03:
=====================================
Writing your first Django app, part 3
=====================================
This tutorial begins where :ref:`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
:ref:`ref-request-response`. For more details on URLconfs, see the
:ref:`topics-http-urls`.
When you ran ``python 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 *
urlpatterns = patterns('',
(r'^polls/$', 'mysite.polls.views.index'),
(r'^polls/(?P<poll_id>\d+)/$', 'mysite.polls.views.detail'),
(r'^polls/(?P<poll_id>\d+)/results/$', 'mysite.polls.views.results'),
(r'^polls/(?P<poll_id>\d+)/vote/$', 'mysite.polls.views.vote'),
)
This is worth a review. When somebody requests a page from your Web site -- say,
"/polls/23/", Django will load this Python module, because it's pointed to by
the :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
associated Python package/module: ``mysite.polls.views.detail``. That
corresponds to the function ``detail()`` in ``mysite/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 parenthesis
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$', 'mysite.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://www.python.org/doc/current/lib/module-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 mysite.polls.views. Error was: 'module'
object has no attribute 'index'
This error happened because you haven't written a function ``index()`` in the
module ``mysite/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 ``mysite/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 add the following view. It's slightly different, because it takes 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)
Take a look in your browser, at "/polls/34/". It'll display whatever ID you
provide in the URL.
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 :ref:`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 mysite.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 mysite.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>{{ poll.question }}</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.
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 mysite.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.
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
module 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.
Three more things to note about 404 views:
* 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 ``True`` (in your settings module) then your
404 view will never be used, and the traceback will be displayed instead.
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 :ref:`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/$', 'mysite.polls.views.index'),
(r'^polls/(?P<poll_id>\d+)/$', 'mysite.polls.views.detail'),
(r'^polls/(?P<poll_id>\d+)/results/$', 'mysite.polls.views.results'),
(r'^polls/(?P<poll_id>\d+)/vote/$', 'mysite.polls.views.vote'),
)
Namely, ``mysite.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('mysite.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.
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 ``mysite/polls/urls.py``. Then, change
``mysite/urls.py`` to remove the poll-specific URLs and insert an
:func:`~django.conf.urls.defaults.include`::
(r'^polls/', include('mysite.polls.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 'mysite.polls.urls' URLconf for
further processing.
Now that we've decoupled that, we need to decouple the 'mysite.polls.urls'
URLconf by removing the leading "polls/" from each line::
urlpatterns = patterns('mysite.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 URL root, and the app will still work.
All the poll app cares about is its relative URLs, not its absolute URLs.
When you're comfortable with writing views, read :ref:`part 4 of this tutorial
<intro-tutorial04>` to learn about simple form processing and generic views.

311
docs/intro/tutorial04.txt Normal file
View File

@@ -0,0 +1,311 @@
.. _intro-tutorial04:
=====================================
Writing your first Django app, part 4
=====================================
This tutorial begins where :ref:`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">
{% 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
Now, let's create a Django view that handles the submitted data and does
something with it. Remember, in :ref:`Tutorial 3 <intro-tutorial03>`, we created
a URLconf for the polls application that includes this line::
(r'^(?P<poll_id>\d+)/vote/$', 'mysite.polls.views.vote'),
So let's create a ``vote()`` function in ``mysite/polls/views.py``::
from django.shortcuts import get_object_or_404, render_to_response
from django.http import HttpResponseRedirect
from django.core.urlresolvers import reverse
from mysite.polls.models import Choice, Poll
# ...
def vote(request, poll_id):
p = get_object_or_404(Poll, pk=poll_id)
try:
selected_choice = p.choice_set.get(pk=request.POST['choice'])
except (KeyError, Choice.DoesNotExist):
# Redisplay the poll voting form.
return render_to_response('polls/detail.html', {
'poll': p,
'error_message': "You didn't select a choice.",
})
else:
selected_choice.votes += 1
selected_choice.save()
# Always return an HttpResponseRedirect after successfully dealing
# with POST data. This prevents data from being posted twice if a
# user hits the Back button.
return HttpResponseRedirect(reverse('mysite.polls.views.results', args=(p.id,)))
This code includes a few things we haven't covered yet in this tutorial:
* :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
:ref:`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 :ref:`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>
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 :ref:`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.
.. admonition:: Why the code-shuffle?
Generally, when writing a Django app, you'll evaluate whether generic views
are a good fit for your problem, and you'll use them from the beginning,
rather than refactoring your code halfway through. But this tutorial
intentionally has focused on writing the views "the hard way" until now, to
focus on core concepts.
You should know basic math before you start using a calculator.
First, open the ``polls/urls.py`` URLconf. It looks like this, according to the
tutorial so far::
from django.conf.urls.defaults import *
urlpatterns = patterns('mysite.polls.views',
(r'^$', 'index'),
(r'^(?P<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 mysite.polls.models import Poll
info_dict = {
'queryset': Poll.objects.all(),
}
urlpatterns = patterns('',
(r'^$', 'django.views.generic.list_detail.object_list', info_dict),
(r'^(?P<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/$', 'mysite.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 :func:`~django.views.generic.list_detail.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 :ref:`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 :ref:`where to go
from here <intro-whatsnext>`

235
docs/intro/whatsnext.txt Normal file
View File

@@ -0,0 +1,235 @@
.. _intro-whatsnext:
=================
What to read next
=================
So you've read all the :ref:`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 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 :ref:`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 :ref:`topic guides <topics-index>`, on the other hand, dive deep into
individual parts of Django. There are complete guides to Django's
:ref:`model system <topics-db-index>`, :ref:`template engine
<topics-templates>`, :ref:`forms framework <topics-forms-index>`, and much
more.`
This is probably where you'll want to spent 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 :ref:`how-to guides <howto-index>` that answer
common "How do I ...?" questions. Here you'll find information about
:ref:`generating PDFs with Django <howto-outputting-pdf>`, :ref:`writing
custom template tags <howto-custom-template-tags>`, and more.
Answers to really common questions can also be found in the :ref:`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 :ref:`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 :ref:`release notes <releases-index>`,
:ref:`documentation of obsolete features <obsolete-index>`,
:ref:`internals documentation <internals-index>` for those who want to add
code to Django itself, and a :ref:`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://www.djangoproject.com/documentation/ . 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 "edit_inline" in any
Django document:
.. code-block:: bash
$ grep edit_inline /path/to/django/docs/*.txt
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 website, 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``.
.. warning::
At the time of this writing, Django's using a version of Sphinx not
yet released, so you'll currently need to install Sphinx from the
source. We'll fix this shortly.
__ 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 Django development version**. In practice, this means that the
current documentation on djangoproject.com can be used by users of either
the latest release *or* the development version.
* 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://www.djangoproject.com/documentation/