summaryrefslogtreecommitdiff
path: root/parts/django/docs/intro/tutorial01.txt
diff options
context:
space:
mode:
Diffstat (limited to 'parts/django/docs/intro/tutorial01.txt')
-rw-r--r--parts/django/docs/intro/tutorial01.txt690
1 files changed, 690 insertions, 0 deletions
diff --git a/parts/django/docs/intro/tutorial01.txt b/parts/django/docs/intro/tutorial01.txt
new file mode 100644
index 0000000..560070b
--- /dev/null
+++ b/parts/django/docs/intro/tutorial01.txt
@@ -0,0 +1,690 @@
+=====================================
+Writing your first Django app, part 1
+=====================================
+
+Let's learn by example.
+
+Throughout this tutorial, we'll walk you through the creation of a basic
+poll application.
+
+It'll consist of two parts:
+
+ * A public site that lets people view polls and vote in them.
+ * An admin site that lets you add, change and delete polls.
+
+We'll assume you have :doc:`Django installed </intro/install>` already. You can
+tell Django is installed by running the Python interactive interpreter and
+typing ``import django``. If that command runs successfully, with no errors,
+Django is installed.
+
+.. admonition:: Where to get help:
+
+ If you're having trouble going through this tutorial, please post a message
+ to `django-users`__ or drop by `#django on irc.freenode.net`__ to chat
+ with other Django users who might be able to help.
+
+__ http://groups.google.com/group/django-users
+__ irc://irc.freenode.net/django
+
+Creating a project
+==================
+
+If this is your first time using Django, you'll have to take care of some
+initial setup. Namely, you'll need to auto-generate some code that establishes a
+Django :term:`project` -- a collection of settings for an instance of Django,
+including database configuration, Django-specific options and
+application-specific settings.
+
+From the command line, ``cd`` into a directory where you'd like to store your
+code, then run the command ``django-admin.py startproject mysite``. This will
+create a ``mysite`` directory in your current directory.
+
+.. admonition:: Script name may differ in distribution packages
+
+ If you installed Django using a Linux distribution's package manager
+ (e.g. apt-get or yum) ``django-admin.py`` may have been renamed to
+ ``django-admin``. You may continue through this documentation by omitting
+ ``.py`` from each command.
+
+.. admonition:: Mac OS X permissions
+
+ If you're using Mac OS X, you may see the message "permission denied" when
+ you try to run ``django-admin.py startproject``. This is because, on
+ Unix-based systems like OS X, a file must be marked as "executable" before it
+ can be run as a program. To do this, open Terminal.app and navigate (using
+ the ``cd`` command) to the directory where :doc:`django-admin.py
+ </ref/django-admin>` is installed, then run the command
+ ``chmod +x django-admin.py``.
+
+.. note::
+
+ You'll need to avoid naming projects after built-in Python or Django
+ components. In particular, this means you should avoid using names like
+ ``django`` (which will conflict with Django itself) or ``test`` (which
+ conflicts with a built-in Python package).
+
+:doc:`django-admin.py </ref/django-admin>` should be on your system path if you
+installed Django via ``python setup.py``. If it's not on your path, you can find
+it in ``site-packages/django/bin``, where ```site-packages``` is a directory
+within your Python installation. Consider symlinking to :doc:`django-admin.py
+</ref/django-admin>` from some place on your path, such as
+:file:`/usr/local/bin`.
+
+.. admonition:: Where should this code live?
+
+ If your background is in PHP, you're probably used to putting code under the
+ Web server's document root (in a place such as ``/var/www``). With Django,
+ you don't do that. It's not a good idea to put any of this Python code
+ within your Web server's document root, because it risks the possibility
+ that people may be able to view your code over the Web. That's not good for
+ security.
+
+ Put your code in some directory **outside** of the document root, such as
+ :file:`/home/mycode`.
+
+Let's look at what :djadmin:`startproject` created::
+
+ mysite/
+ __init__.py
+ manage.py
+ settings.py
+ urls.py
+
+These files are:
+
+ * :file:`__init__.py`: An empty file that tells Python that this directory
+ should be considered a Python package. (Read `more about packages`_ in the
+ official Python docs if you're a Python beginner.)
+
+ * :file:`manage.py`: A command-line utility that lets you interact with this
+ Django project in various ways. You can read all the details about
+ :file:`manage.py` in :doc:`/ref/django-admin`.
+
+ * :file:`settings.py`: Settings/configuration for this Django project.
+ :doc:`/topics/settings` will tell you all about how settings work.
+
+ * :file:`urls.py`: The URL declarations for this Django project; a "table of
+ contents" of your Django-powered site. You can read more about URLs in
+ :doc:`/topics/http/urls`.
+
+.. _more about packages: http://docs.python.org/tutorial/modules.html#packages
+
+The development server
+----------------------
+
+Let's verify this worked. Change into the :file:`mysite` directory, if you
+haven't already, and run the command ``python manage.py runserver``. You'll see
+the following output on the command line::
+
+ Validating models...
+ 0 errors found.
+
+ Django version 1.0, using settings 'mysite.settings'
+ Development server is running at http://127.0.0.1:8000/
+ Quit the server with CONTROL-C.
+
+You've started the Django development server, a lightweight Web server written
+purely in Python. We've included this with Django so you can develop things
+rapidly, without having to deal with configuring a production server -- such as
+Apache -- until you're ready for production.
+
+Now's a good time to note: DON'T use this server in anything resembling a
+production environment. It's intended only for use while developing. (We're in
+the business of making Web frameworks, not Web servers.)
+
+Now that the server's running, visit http://127.0.0.1:8000/ with your Web
+browser. You'll see a "Welcome to Django" page, in pleasant, light-blue pastel.
+It worked!
+
+.. admonition:: Changing the port
+
+ By default, the :djadmin:`runserver` command starts the development server
+ on the internal IP at port 8000.
+
+ If you want to change the server's port, pass
+ it as a command-line argument. For instance, this command starts the server
+ on port 8080:
+
+ .. code-block:: bash
+
+ python manage.py runserver 8080
+
+ If you want to change the server's IP, pass it along with the port. So to
+ listen on all public IPs (useful if you want to show off your work on other
+ computers), use:
+
+ .. code-block:: bash
+
+ python manage.py runserver 0.0.0.0:8000
+
+ Full docs for the development server can be found in the
+ :djadmin:`runserver` reference.
+
+Database setup
+--------------
+
+Now, edit :file:`settings.py`. It's a normal Python module with
+module-level variables representing Django settings. Change the
+following keys in the :setting:`DATABASES` ``'default'`` item to match
+your databases connection settings.
+
+ * :setting:`ENGINE` -- Either
+ ``'django.db.backends.postgresql_psycopg2'``,
+ ``'django.db.backends.mysql'`` or
+ ``'django.db.backends.sqlite3'``. Other backends are
+ :setting:`also available <ENGINE>`.
+
+ * :setting:`NAME` -- The name of your database. If you're using
+ SQLite, the database will be a file on your computer; in that
+ case, :setting:`NAME` should be the full absolute path,
+ including filename, of that file. If the file doesn't exist, it
+ will automatically be created when you synchronize the database
+ for the first time (see below).
+
+ When specifying the path, always use forward slashes, even on
+ Windows (e.g. ``C:/homes/user/mysite/sqlite3.db``).
+
+ * :setting:`USER` -- Your database username (not used for SQLite).
+
+ * :setting:`PASSWORD` -- Your database password (not used for
+ SQLite).
+
+ * :setting:`HOST` -- The host your database is on. Leave this as
+ an empty string if your database server is on the same physical
+ machine (not used for SQLite).
+
+If you're new to databases, we recommend simply using SQLite (by
+setting :setting:`ENGINE` to ``'django.db.backends.sqlite3'``). SQLite
+is included as part of Python 2.5 and later, so you won't need to
+install anything else.
+
+.. note::
+
+ If you're using PostgreSQL or MySQL, make sure you've created a database by
+ this point. Do that with "``CREATE DATABASE database_name;``" within your
+ database's interactive prompt.
+
+ If you're using SQLite, you don't need to create anything beforehand - the
+ database file will be created automatically when it is needed.
+
+While you're editing :file:`settings.py`, take note of the
+:setting:`INSTALLED_APPS` setting towards the bottom of the file. That variable
+holds the names of all Django applications that are activated in this Django
+instance. Apps can be used in multiple projects, and you can package and
+distribute them for use by others in their projects.
+
+By default, :setting:`INSTALLED_APPS` contains the following apps, all of which
+come with Django:
+
+ * :mod:`django.contrib.auth` -- An authentication system.
+
+ * :mod:`django.contrib.contenttypes` -- A framework for content types.
+
+ * :mod:`django.contrib.sessions` -- A session framework.
+
+ * :mod:`django.contrib.sites` -- A framework for managing multiple sites
+ with one Django installation.
+
+ * :mod:`django.contrib.messages` -- A messaging framework.
+
+These applications are included by default as a convenience for the common case.
+
+Each of these applications makes use of at least one database table, though,
+so we need to create the tables in the database before we can use them. To do
+that, run the following command:
+
+.. code-block:: bash
+
+ python manage.py syncdb
+
+The :djadmin:`syncdb` command looks at the :setting:`INSTALLED_APPS` setting and
+creates any necessary database tables according to the database settings in your
+:file:`settings.py` file. You'll see a message for each database table it
+creates, and you'll get a prompt asking you if you'd like to create a superuser
+account for the authentication system. Go ahead and do that.
+
+If you're interested, run the command-line client for your database and type
+``\dt`` (PostgreSQL), ``SHOW TABLES;`` (MySQL), or ``.schema`` (SQLite) to
+display the tables Django created.
+
+.. admonition:: For the minimalists
+
+ Like we said above, the default applications are included for the common
+ case, but not everybody needs them. If you don't need any or all of them,
+ feel free to comment-out or delete the appropriate line(s) from
+ :setting:`INSTALLED_APPS` before running :djadmin:`syncdb`. The
+ :djadmin:`syncdb` command will only create tables for apps in
+ :setting:`INSTALLED_APPS`.
+
+.. _creating-models:
+
+Creating models
+===============
+
+Now that your environment -- a "project" -- is set up, you're set to start
+doing work.
+
+Each application you write in Django consists of a Python package, somewhere
+on your `Python path`_, that follows a certain convention. Django comes with a
+utility that automatically generates the basic directory structure of an app,
+so you can focus on writing code rather than creating directories.
+
+.. admonition:: Projects vs. apps
+
+ What's the difference between a project and an app? An app is a Web
+ application that does something -- e.g., a Weblog system, a database of
+ public records or a simple poll app. A project is a collection of
+ configuration and apps for a particular Web site. A project can contain
+ multiple apps. An app can be in multiple projects.
+
+Your apps can live anywhere on your `Python path`_. In this tutorial, we'll
+create our poll app in the :file:`mysite` directory for simplicity.
+
+To create your app, make sure you're in the :file:`mysite` directory and type
+this command:
+
+.. code-block:: bash
+
+ python manage.py startapp polls
+
+That'll create a directory :file:`polls`, which is laid out like this::
+
+ polls/
+ __init__.py
+ models.py
+ tests.py
+ views.py
+
+This directory structure will house the poll application.
+
+The first step in writing a database Web app in Django is to define your models
+-- essentially, your database layout, with additional metadata.
+
+.. admonition:: Philosophy
+
+ A model is the single, definitive source of data about your data. It contains
+ the essential fields and behaviors of the data you're storing. Django follows
+ the :ref:`DRY Principle <dry>`. The goal is to define your data model in one
+ place and automatically derive things from it.
+
+In our simple poll app, we'll create two models: polls and choices. A poll has
+a question and a publication date. A choice has two fields: the text of the
+choice and a vote tally. Each choice is associated with a poll.
+
+These concepts are represented by simple Python classes. Edit the
+:file:`polls/models.py` file so it looks like this::
+
+ from django.db import models
+
+ class Poll(models.Model):
+ question = models.CharField(max_length=200)
+ pub_date = models.DateTimeField('date published')
+
+ class Choice(models.Model):
+ poll = models.ForeignKey(Poll)
+ choice = models.CharField(max_length=200)
+ votes = models.IntegerField()
+
+The code is straightforward. Each model is represented by a class that
+subclasses :class:`django.db.models.Model`. Each model has a number of class
+variables, each of which represents a database field in the model.
+
+Each field is represented by an instance of a :class:`~django.db.models.Field`
+class -- e.g., :class:`~django.db.models.CharField` for character fields and
+:class:`~django.db.models.DateTimeField` for datetimes. This tells Django what
+type of data each field holds.
+
+The name of each :class:`~django.db.models.Field` instance (e.g. ``question`` or
+``pub_date`` ) is the field's name, in machine-friendly format. You'll use this
+value in your Python code, and your database will use it as the column name.
+
+You can use an optional first positional argument to a
+:class:`~django.db.models.Field` to designate a human-readable name. That's used
+in a couple of introspective parts of Django, and it doubles as documentation.
+If this field isn't provided, Django will use the machine-readable name. In this
+example, we've only defined a human-readable name for ``Poll.pub_date``. For all
+other fields in this model, the field's machine-readable name will suffice as
+its human-readable name.
+
+Some :class:`~django.db.models.Field` classes have required elements.
+:class:`~django.db.models.CharField`, for example, requires that you give it a
+:attr:`~django.db.models.Field.max_length`. That's used not only in the database
+schema, but in validation, as we'll soon see.
+
+Finally, note a relationship is defined, using
+:class:`~django.db.models.ForeignKey`. That tells Django each Choice is related
+to a single Poll. Django supports all the common database relationships:
+many-to-ones, many-to-manys and one-to-ones.
+
+.. _`Python path`: http://docs.python.org/tutorial/modules.html#the-module-search-path
+
+Activating models
+=================
+
+That small bit of model code gives Django a lot of information. With it, Django
+is able to:
+
+ * Create a database schema (``CREATE TABLE`` statements) for this app.
+ * Create a Python database-access API for accessing Poll and Choice objects.
+
+But first we need to tell our project that the ``polls`` app is installed.
+
+.. admonition:: Philosophy
+
+ Django apps are "pluggable": You can use an app in multiple projects, and
+ you can distribute apps, because they don't have to be tied to a given
+ Django installation.
+
+Edit the :file:`settings.py` file again, and change the
+:setting:`INSTALLED_APPS` setting to include the string ``'polls'``. So
+it'll look like this::
+
+ INSTALLED_APPS = (
+ 'django.contrib.auth',
+ 'django.contrib.contenttypes',
+ 'django.contrib.sessions',
+ 'django.contrib.sites',
+ 'polls'
+ )
+
+Now Django knows to include the ``polls`` app. Let's run another
+command:
+
+.. code-block:: bash
+
+ python manage.py sql polls
+
+You should see something similar to the following (the ``CREATE TABLE`` SQL
+statements for the polls app):
+
+.. code-block:: sql
+
+ BEGIN;
+ CREATE TABLE "polls_poll" (
+ "id" serial NOT NULL PRIMARY KEY,
+ "question" varchar(200) NOT NULL,
+ "pub_date" timestamp with time zone NOT NULL
+ );
+ CREATE TABLE "polls_choice" (
+ "id" serial NOT NULL PRIMARY KEY,
+ "poll_id" integer NOT NULL REFERENCES "polls_poll" ("id"),
+ "choice" varchar(200) NOT NULL,
+ "votes" integer NOT NULL
+ );
+ COMMIT;
+
+Note the following:
+
+ * The exact output will vary depending on the database you are using.
+
+ * Table names are automatically generated by combining the name of the app
+ (``polls``) and the lowercase name of the model -- ``poll`` and
+ ``choice``. (You can override this behavior.)
+
+ * Primary keys (IDs) are added automatically. (You can override this, too.)
+
+ * By convention, Django appends ``"_id"`` to the foreign key field name.
+ Yes, you can override this, as well.
+
+ * The foreign key relationship is made explicit by a ``REFERENCES``
+ statement.
+
+ * It's tailored to the database you're using, so database-specific field
+ types such as ``auto_increment`` (MySQL), ``serial`` (PostgreSQL), or
+ ``integer primary key`` (SQLite) are handled for you automatically. Same
+ goes for quoting of field names -- e.g., using double quotes or single
+ quotes. The author of this tutorial runs PostgreSQL, so the example
+ output is in PostgreSQL syntax.
+
+ * The :djadmin:`sql` command doesn't actually run the SQL in your database -
+ it just prints it to the screen so that you can see what SQL Django thinks
+ is required. If you wanted to, you could copy and paste this SQL into your
+ database prompt. However, as we will see shortly, Django provides an
+ easier way of committing the SQL to the database.
+
+If you're interested, also run the following commands:
+
+ * :djadmin:`python manage.py validate <validate>` -- Checks for any errors
+ in the construction of your models.
+
+ * :djadmin:`python manage.py sqlcustom polls <sqlcustom>` -- Outputs any
+ :ref:`custom SQL statements <initial-sql>` (such as table modifications or
+ constraints) that are defined for the application.
+
+ * :djadmin:`python manage.py sqlclear polls <sqlclear>` -- Outputs the
+ necessary ``DROP TABLE`` statements for this app, according to which
+ tables already exist in your database (if any).
+
+ * :djadmin:`python manage.py sqlindexes polls <sqlindexes>` -- Outputs the
+ ``CREATE INDEX`` statements for this app.
+
+ * :djadmin:`python manage.py sqlall polls <sqlall>` -- A combination of all
+ the SQL from the :djadmin:`sql`, :djadmin:`sqlcustom`, and
+ :djadmin:`sqlindexes` commands.
+
+Looking at the output of those commands can help you understand what's actually
+happening under the hood.
+
+Now, run :djadmin:`syncdb` again to create those model tables in your database:
+
+.. code-block:: bash
+
+ python manage.py syncdb
+
+The :djadmin:`syncdb` command runs the sql from 'sqlall' on your database for
+all apps in :setting:`INSTALLED_APPS` that don't already exist in your database.
+This creates all the tables, initial data and indexes for any apps you have
+added to your project since the last time you ran syncdb. :djadmin:`syncdb` can
+be called as often as you like, and it will only ever create the tables that
+don't exist.
+
+Read the :doc:`django-admin.py documentation </ref/django-admin>` for full
+information on what the ``manage.py`` utility can do.
+
+Playing with the API
+====================
+
+Now, let's hop into the interactive Python shell and play around with the free
+API Django gives you. To invoke the Python shell, use this command:
+
+.. code-block:: bash
+
+ python manage.py shell
+
+We're using this instead of simply typing "python", because ``manage.py`` sets
+up the project's environment for you. "Setting up the environment" involves two
+things:
+
+ * Putting ``polls`` on ``sys.path``. For flexibility, several pieces of
+ Django refer to projects in Python dotted-path notation (e.g.
+ ``'polls.models'``). In order for this to work, the ``polls``
+ package has to be on ``sys.path``.
+
+ We've already seen one example of this: the :setting:`INSTALLED_APPS`
+ setting is a list of packages in dotted-path notation.
+
+ * Setting the ``DJANGO_SETTINGS_MODULE`` environment variable, which gives
+ Django the path to your ``settings.py`` file.
+
+.. admonition:: Bypassing manage.py
+
+ If you'd rather not use ``manage.py``, no problem. Just make sure ``mysite``
+ and ``polls`` are at the root level on the Python path (i.e., ``import mysite``
+ and ``import polls`` work) and set the ``DJANGO_SETTINGS_MODULE`` environment
+ variable to ``mysite.settings``.
+
+ For more information on all of this, see the :doc:`django-admin.py
+ documentation </ref/django-admin>`.
+
+Once you're in the shell, explore the :doc:`database API </topics/db/queries>`::
+
+ >>> from polls.models import Poll, Choice # Import the model classes we just wrote.
+
+ # No polls are in the system yet.
+ >>> Poll.objects.all()
+ []
+
+ # Create a new Poll.
+ >>> import datetime
+ >>> p = Poll(question="What's up?", pub_date=datetime.datetime.now())
+
+ # Save the object into the database. You have to call save() explicitly.
+ >>> p.save()
+
+ # Now it has an ID. Note that this might say "1L" instead of "1", depending
+ # on which database you're using. That's no biggie; it just means your
+ # database backend prefers to return integers as Python long integer
+ # objects.
+ >>> p.id
+ 1
+
+ # Access database columns via Python attributes.
+ >>> p.question
+ "What's up?"
+ >>> p.pub_date
+ datetime.datetime(2007, 7, 15, 12, 00, 53)
+
+ # Change values by changing the attributes, then calling save().
+ >>> p.pub_date = datetime.datetime(2007, 4, 1, 0, 0)
+ >>> p.save()
+
+ # objects.all() displays all the polls in the database.
+ >>> Poll.objects.all()
+ [<Poll: Poll object>]
+
+
+Wait a minute. ``<Poll: Poll object>`` is, utterly, an unhelpful representation
+of this object. Let's fix that by editing the polls model (in the
+``polls/models.py`` file) and adding a
+:meth:`~django.db.models.Model.__unicode__` method to both ``Poll`` and
+``Choice``::
+
+ class Poll(models.Model):
+ # ...
+ def __unicode__(self):
+ return self.question
+
+ class Choice(models.Model):
+ # ...
+ def __unicode__(self):
+ return self.choice
+
+It's important to add :meth:`~django.db.models.Model.__unicode__` methods to
+your models, not only for your own sanity when dealing with the interactive
+prompt, but also because objects' representations are used throughout Django's
+automatically-generated admin.
+
+.. admonition:: Why :meth:`~django.db.models.Model.__unicode__` and not
+ :meth:`~django.db.models.Model.__str__`?
+
+ If you're familiar with Python, you might be in the habit of adding
+ :meth:`~django.db.models.Model.__str__` methods to your classes, not
+ :meth:`~django.db.models.Model.__unicode__` methods. We use
+ :meth:`~django.db.models.Model.__unicode__` here because Django models deal
+ with Unicode by default. All data stored in your database is converted to
+ Unicode when it's returned.
+
+ Django models have a default :meth:`~django.db.models.Model.__str__` method
+ that calls :meth:`~django.db.models.Model.__unicode__` and converts the
+ result to a UTF-8 bytestring. This means that ``unicode(p)`` will return a
+ Unicode string, and ``str(p)`` will return a normal string, with characters
+ encoded as UTF-8.
+
+ If all of this is jibberish to you, just remember to add
+ :meth:`~django.db.models.Model.__unicode__` methods to your models. With any
+ luck, things should Just Work for you.
+
+Note these are normal Python methods. Let's add a custom method, just for
+demonstration::
+
+ import datetime
+ # ...
+ class Poll(models.Model):
+ # ...
+ def was_published_today(self):
+ return self.pub_date.date() == datetime.date.today()
+
+Note the addition of ``import datetime`` to reference Python's standard
+``datetime`` module.
+
+Save these changes and start a new Python interactive shell by running
+``python manage.py shell`` again::
+
+ >>> from polls.models import Poll, Choice
+
+ # Make sure our __unicode__() addition worked.
+ >>> Poll.objects.all()
+ [<Poll: What's up?>]
+
+ # Django provides a rich database lookup API that's entirely driven by
+ # keyword arguments.
+ >>> Poll.objects.filter(id=1)
+ [<Poll: What's up?>]
+ >>> Poll.objects.filter(question__startswith='What')
+ [<Poll: What's up?>]
+
+ # Get the poll whose year is 2007.
+ >>> Poll.objects.get(pub_date__year=2007)
+ <Poll: What's up?>
+
+ >>> Poll.objects.get(id=2)
+ Traceback (most recent call last):
+ ...
+ DoesNotExist: Poll matching query does not exist.
+
+ # Lookup by a primary key is the most common case, so Django provides a
+ # shortcut for primary-key exact lookups.
+ # The following is identical to Poll.objects.get(id=1).
+ >>> Poll.objects.get(pk=1)
+ <Poll: What's up?>
+
+ # Make sure our custom method worked.
+ >>> p = Poll.objects.get(pk=1)
+ >>> p.was_published_today()
+ False
+
+ # Give the Poll a couple of Choices. The create call constructs a new
+ # choice object, does the INSERT statement, adds the choice to the set
+ # of available choices and returns the new Choice object. Django creates
+ # a set to hold the "other side" of a ForeignKey relation
+ # (e.g. a poll's choices) which can be accessed via the API.
+ >>> p = Poll.objects.get(pk=1)
+
+ # Display any choices from the related object set -- none so far.
+ >>> p.choice_set.all()
+ []
+
+ # Create three choices.
+ >>> p.choice_set.create(choice='Not much', votes=0)
+ <Choice: Not much>
+ >>> p.choice_set.create(choice='The sky', votes=0)
+ <Choice: The sky>
+ >>> c = p.choice_set.create(choice='Just hacking again', votes=0)
+
+ # Choice objects have API access to their related Poll objects.
+ >>> c.poll
+ <Poll: What's up?>
+
+ # And vice versa: Poll objects get access to Choice objects.
+ >>> p.choice_set.all()
+ [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]
+ >>> p.choice_set.count()
+ 3
+
+ # The API automatically follows relationships as far as you need.
+ # Use double underscores to separate relationships.
+ # This works as many levels deep as you want; there's no limit.
+ # Find all Choices for any poll whose pub_date is in 2007.
+ >>> Choice.objects.filter(poll__pub_date__year=2007)
+ [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]
+
+ # Let's delete one of the choices. Use delete() for that.
+ >>> c = p.choice_set.filter(choice__startswith='Just hacking')
+ >>> c.delete()
+
+For more information on model relations, see :doc:`Accessing related objects
+</ref/models/relations>`. For full details on the database API, see our
+:doc:`Database API reference </topics/db/queries>`.
+
+When you're comfortable with the API, read :doc:`part 2 of this tutorial
+</intro/tutorial02>` to get Django's automatic admin working.