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
BIN
docs/intro/_images/admin01.png
Normal file
BIN
docs/intro/_images/admin02.png
Normal file
BIN
docs/intro/_images/admin02t.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
docs/intro/_images/admin03.png
Normal file
BIN
docs/intro/_images/admin03t.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
docs/intro/_images/admin04.png
Normal file
BIN
docs/intro/_images/admin04t.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
docs/intro/_images/admin05.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
docs/intro/_images/admin05t.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
docs/intro/_images/admin06.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
docs/intro/_images/admin06t.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
docs/intro/_images/admin07.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
docs/intro/_images/admin08.png
Normal file
|
After Width: | Height: | Size: 31 KiB |
BIN
docs/intro/_images/admin08t.png
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
docs/intro/_images/admin09.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
docs/intro/_images/admin10.png
Normal file
|
After Width: | Height: | Size: 5.4 KiB |
BIN
docs/intro/_images/admin11.png
Normal file
|
After Width: | Height: | Size: 33 KiB |
BIN
docs/intro/_images/admin11t.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
docs/intro/_images/admin12.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
docs/intro/_images/admin13.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
docs/intro/_images/admin13t.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
docs/intro/_images/admin14.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
docs/intro/_images/admin14t.png
Normal file
|
After Width: | Height: | Size: 23 KiB |
38
docs/intro/index.txt
Normal 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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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/
|
||||