mirror of
				https://github.com/django/django.git
				synced 2025-10-31 09:41:08 +00:00 
			
		
		
		
	git-svn-id: http://code.djangoproject.com/svn/django/trunk@1465 bcc190cf-cafb-0310-a4f2-bffc1f526a37
		
			
				
	
	
		
			436 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			436 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
| =====================================
 | |
| Writing your first Django app, part 1
 | |
| =====================================
 | |
| 
 | |
| By Adrian Holovaty <holovaty@gmail.com>
 | |
| 
 | |
| Let's learn by example.
 | |
| 
 | |
| Throughout this tutorial, we'll walk you through the creation of a simple Web
 | |
| poll application.
 | |
| 
 | |
| It'll consist of two parts:
 | |
| 
 | |
| * A public site that lets people vote in polls and view poll results.
 | |
| * An admin site that lets you add, change and delete polls behind the scenes.
 | |
| 
 | |
| We'll assume you have `Django installed`_ already.
 | |
| 
 | |
| .. _`Django installed`: http://www.djangoproject.com/documentation/install/
 | |
| 
 | |
| Initial setup
 | |
| =============
 | |
| 
 | |
| If this is your first time using Django, you'll have to take care of some
 | |
| initial setup.
 | |
| 
 | |
| Run the command ``django-admin.py startproject myproject``. That'll create a
 | |
| ``myproject`` directory in your current directory.
 | |
| 
 | |
| (``django-admin.py`` should be on your system path if you installed Django via
 | |
| its setup.py utility. If it's not on your path, you can find it in
 | |
| ``site-packages/django/bin``; consider symlinking to it from some place
 | |
| on your path, such as /usr/local/bin.)
 | |
| 
 | |
| A project is a collection of settings for an instance of Django -- including
 | |
| database configuration, Django-specific options and application-specific
 | |
| settings. Let's look at what ``startproject`` created::
 | |
| 
 | |
|     myproject/
 | |
|         __init__.py
 | |
|         apps/
 | |
|             __init__.py
 | |
|         settings.py
 | |
|         urls.py
 | |
| 
 | |
| First, edit ``myproject/settings.py``. It's a normal Python module with
 | |
| module-level variables representing Django settings. Edit the file and change
 | |
| these settings to match your database's connection parameters:
 | |
| 
 | |
|     * ``DATABASE_ENGINE`` -- Either 'postgresql', 'mysql' or 'sqlite3'.
 | |
|       More coming soon.
 | |
|     * ``DATABASE_NAME`` -- The name of your database, or the full (absolute)
 | |
|       path to the database file if you're using sqlite.
 | |
|     * ``DATABASE_USER`` -- Your database username (not used for sqlite).
 | |
|     * ``DATABASE_PASSWORD`` -- Your database password (not used for sqlite).
 | |
|     * ``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).
 | |
| 
 | |
| .. admonition:: Note
 | |
| 
 | |
|     Make sure you've created a database within PostgreSQL or MySQL by this
 | |
|     point. Do that with "``CREATE DATABASE database_name;``" within your
 | |
|     database's interactive prompt.
 | |
| 
 | |
| Now, take a second to make sure ``myproject`` is on your Python path. You
 | |
| can do this by copying ``myproject`` to Python's ``site-packages`` directory,
 | |
| or you can do it by altering the ``PYTHONPATH`` environment variable. See the
 | |
| `Python path documentation`_ for more information. If you opt to set the
 | |
| ``PYTHONPATH`` environment variable, note that you'll need to set it to the
 | |
| *parent* directory of ``myproject``. (You can test this by typing
 | |
| "import myproject" into the Python interactive prompt.)
 | |
| 
 | |
| Run the following command::
 | |
| 
 | |
|     django-admin.py init --settings=myproject.settings
 | |
| 
 | |
| The ``django-admin.py`` utility generally needs to know which settings module
 | |
| you're using. Here, we're doing that by specifying ``settings=`` on the command
 | |
| line, but that can get tedious. If you don't want to type ``settings=`` each
 | |
| time, you can set the ``DJANGO_SETTINGS_MODULE`` environment variable. Here's
 | |
| how you do that in the Bash shell on Unix::
 | |
| 
 | |
|     export DJANGO_SETTINGS_MODULE=myproject.settings
 | |
| 
 | |
| On Windows, you'd use ``set`` instead::
 | |
| 
 | |
|     set DJANGO_SETTINGS_MODULE=myproject.settings
 | |
| 
 | |
| If you don't see any errors after running ``django-admin.py init``, you know it
 | |
| worked. That command initialized your database with Django's core database
 | |
| tables. 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.
 | |
| 
 | |
| .. _`Python path documentation`: http://docs.python.org/tut/node8.html#SECTION008110000000000000000
 | |
| 
 | |
| Creating models
 | |
| ===============
 | |
| 
 | |
| Now that your environment -- a "project" -- is set up, you're set to start
 | |
| doing work. (You won't have to take care of this boring administrative stuff
 | |
| again.)
 | |
| 
 | |
| Each application you write in Django -- e.g., a weblog system, a database of
 | |
| public records or a simple poll app -- 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.
 | |
| 
 | |
| In this tutorial, we'll create our poll app in the ``myproject/apps``
 | |
| 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
 | |
| ``myproject.apps.polls``. Later in this tutorial, we'll discuss decoupling
 | |
| your apps for distribution.
 | |
| 
 | |
| To create your app, change into the ``myproject/apps`` directory and type this
 | |
| command::
 | |
| 
 | |
|     django-admin.py startapp polls
 | |
| 
 | |
| (From now on, this tutorial will leave out the ``--settings`` parameter and
 | |
| will assume you've either set your ``DJANGO_SETTINGS_MODULE`` environment
 | |
| variable or included the ``--settings`` option in your call to the command.)
 | |
| 
 | |
| That'll create a directory structure like this::
 | |
| 
 | |
|     polls/
 | |
|         __init__.py
 | |
|         models/
 | |
|             __init__.py
 | |
|             polls.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 `DRY Principle`_. 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
 | |
| ``polls/models/polls.py`` file so it looks like this::
 | |
| 
 | |
|     from django.core import meta
 | |
| 
 | |
|     class Poll(meta.Model):
 | |
|         question = meta.CharField(maxlength=200)
 | |
|         pub_date = meta.DateTimeField('date published')
 | |
| 
 | |
|     class Choice(meta.Model):
 | |
|         poll = meta.ForeignKey(Poll)
 | |
|         choice = meta.CharField(maxlength=200)
 | |
|         votes = meta.IntegerField()
 | |
| 
 | |
| The code is straightforward. Each model is represented by a class that
 | |
| subclasses ``django.core.meta.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 ``meta.*Field`` class -- e.g.,
 | |
| ``meta.CharField`` for character fields and ``meta.DateTimeField`` for
 | |
| datetimes. This tells Django what type of data each field holds.
 | |
| 
 | |
| The name of each ``meta.*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 ``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 ``meta.*Field`` classes have required elements. ``meta.CharField``, for
 | |
| example, requires that you give it a ``maxlength``. That's used not only in the
 | |
| database schema, but in validation, as we'll soon see.
 | |
| 
 | |
| Finally, note a relationship is defined, using ``meta.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.
 | |
| 
 | |
| .. _DRY Principle: http://c2.com/cgi/wiki?DontRepeatYourself
 | |
| 
 | |
| 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 myproject/settings.py file again, and change the ``INSTALLED_APPS``
 | |
| setting to include the string "myproject.apps.polls". So it'll look like this::
 | |
| 
 | |
|     INSTALLED_APPS = (
 | |
|         'myproject.apps.polls',
 | |
|     )
 | |
| 
 | |
| (Don't forget the trailing comma because of Python's rules about single-value
 | |
| tuples.)
 | |
| 
 | |
| Now Django knows myproject includes the polls app. Let's run another command::
 | |
| 
 | |
|     django-admin.py sql polls
 | |
| 
 | |
| (Note that it doesn't matter which directory you're in when you run this command.)
 | |
| 
 | |
| You should see the following (the CREATE TABLE SQL statements for the polls app)::
 | |
| 
 | |
|     BEGIN;
 | |
|     CREATE TABLE "polls_polls" (
 | |
|         "id" serial NOT NULL PRIMARY KEY,
 | |
|         "question" varchar(200) NOT NULL,
 | |
|         "pub_date" timestamp with time zone NOT NULL
 | |
|     );
 | |
|     CREATE TABLE "polls_choices" (
 | |
|         "id" serial NOT NULL PRIMARY KEY,
 | |
|         "poll_id" integer NOT NULL REFERENCES "polls_polls" ("id"),
 | |
|         "choice" varchar(200) NOT NULL,
 | |
|         "votes" integer NOT NULL
 | |
|     );
 | |
|     COMMIT;
 | |
| 
 | |
| Note the following:
 | |
| 
 | |
|     * Table names are automatically generated by combining the name of the app
 | |
|       (polls) with a plural version of the object name (polls and choices). (You
 | |
|       can override this behavior.)
 | |
| 
 | |
|     * Primary keys (IDs) are added automatically. (You can override this, too.)
 | |
| 
 | |
|     * Django appends ``"_id"`` to the foreign key field name, by convention.
 | |
|       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.
 | |
| 
 | |
| If you're interested, also run the following commands:
 | |
| 
 | |
|     * ``django-admin.py sqlinitialdata polls`` -- Outputs the initial-data
 | |
|       inserts required for Django's admin framework.
 | |
| 
 | |
|     * ``django-admin.py sqlclear polls`` -- Outputs the necessary ``DROP
 | |
|       TABLE`` statements for this app, according to which tables already exist
 | |
|       in your database (if any).
 | |
| 
 | |
|     * ``django-admin.py sqlindexes polls`` -- Outputs the ``CREATE INDEX``
 | |
|       statements for this app.
 | |
| 
 | |
|     * ``django-admin.py sqlall polls`` -- A combination of 'sql' and
 | |
|       'sqlinitialdata'.
 | |
| 
 | |
| Looking at the output of those commands can help you understand what's actually
 | |
| happening under the hood.
 | |
| 
 | |
| Now, run this command to create the database tables for the polls app
 | |
| automatically::
 | |
| 
 | |
|     django-admin.py install polls
 | |
| 
 | |
| Behind the scenes, all that command does is take the output of
 | |
| ``django-admin.py sqlall polls`` and execute it in the database pointed-to by
 | |
| your Django settings file.
 | |
| 
 | |
| Read the `django-admin.py documentation`_ for full information on what this
 | |
| utility can do.
 | |
| 
 | |
| .. _django-admin.py documentation: http://www.djangoproject.com/documentation/django_admin/
 | |
| 
 | |
| Playing with the API
 | |
| ====================
 | |
| 
 | |
| Now, make sure your DJANGO_SETTINGS_MODULE environment variable is set (as
 | |
| explained above), and open the Python interactive shell to play around with the
 | |
| free Python API Django gives you::
 | |
| 
 | |
|     # Modules are dynamically created within django.models.
 | |
|     # Their names are plural versions of the model class names.
 | |
|     >>> from django.models.polls import polls, choices
 | |
| 
 | |
|     # No polls are in the system yet.
 | |
|     >>> polls.get_list()
 | |
|     []
 | |
| 
 | |
|     # Create a new Poll.
 | |
|     >>> from datetime import datetime
 | |
|     >>> p = polls.Poll(question="What's up?", pub_date=datetime.now())
 | |
| 
 | |
|     # Save the object into the database. You have to call save() explicitly.
 | |
|     >>> p.save()
 | |
| 
 | |
|     # Now it has an ID.
 | |
|     >>> p.id
 | |
|     1
 | |
| 
 | |
|     # Access database columns via Python attributes.
 | |
|     >>> p.question
 | |
|     "What's up?"
 | |
|     >>> p.pub_date
 | |
|     datetime.datetime(2005, 7, 15, 12, 00, 53)
 | |
| 
 | |
|     # Change values by changing the attributes, then calling save().
 | |
|     >>> p.pub_date = datetime(2005, 4, 1, 0, 0)
 | |
|     >>> p.save()
 | |
| 
 | |
|     # get_list() displays all the polls in the database.
 | |
|     >>> polls.get_list()
 | |
|     [<Poll object>]
 | |
| 
 | |
| Wait a minute. ``<Poll object>`` is, utterly, an unhelpful representation of
 | |
| this object. Let's fix that by editing the polls model
 | |
| (in the ``polls/models/polls.py`` file) and adding a ``__repr__()`` method to
 | |
| both ``Poll`` and ``Choice``::
 | |
| 
 | |
|     class Poll(meta.Model):
 | |
|         # ...
 | |
|         def __repr__(self):
 | |
|             return self.question
 | |
| 
 | |
|     class Choice(meta.Model):
 | |
|         # ...
 | |
|         def __repr__(self):
 | |
|             return self.choice
 | |
| 
 | |
| It's important to add ``__repr__()`` 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.
 | |
| 
 | |
| Note these are normal Python methods. Let's add a custom method, just for
 | |
| demonstration::
 | |
| 
 | |
|     class Poll(meta.Model):
 | |
|         # ...
 | |
|         def was_published_today(self):
 | |
|             return self.pub_date.date() == datetime.date.today()
 | |
| 
 | |
| Note ``import datetime`` wasn't necessary. Each model method has access to
 | |
| a handful of commonly-used variables for convenience, including the
 | |
| ``datetime`` module from the Python standard library.
 | |
| 
 | |
| Let's jump back into the Python interactive shell::
 | |
| 
 | |
|     >>> from django.models.polls import polls, choices
 | |
|     # Make sure our __repr__() addition worked.
 | |
|     >>> polls.get_list()
 | |
|     [What's up?]
 | |
| 
 | |
|     # Django provides a rich database lookup API that's entirely driven by
 | |
|     # keyword arguments.
 | |
|     >>> polls.get_object(id__exact=1)
 | |
|     What's up?
 | |
|     >>> polls.get_object(question__startswith='What')
 | |
|     What's up?
 | |
|     >>> polls.get_object(pub_date__year=2005)
 | |
|     What's up?
 | |
|     >>> polls.get_object(id__exact=2)
 | |
|     Traceback (most recent call last):
 | |
|         ...
 | |
|     PollDoesNotExist: Poll does not exist for {'id__exact': 2}
 | |
|     >>> polls.get_list(question__startswith='What')
 | |
|     [What's up?]
 | |
| 
 | |
|     # Lookup by a primary key is the most common case, so Django provides a
 | |
|     # shortcut for primary-key exact lookups.
 | |
|     # The following is identical to polls.get_object(id__exact=1).
 | |
|     >>> polls.get_object(pk=1)
 | |
|     What's up?
 | |
| 
 | |
|     # Make sure our custom method worked.
 | |
|     >>> p = polls.get_object(pk=1)
 | |
|     >>> p.was_published_today()
 | |
|     False
 | |
| 
 | |
|     # Give the Poll a couple of Choices. Each one of these method calls does an
 | |
|     # INSERT statement behind the scenes and returns the new Choice object.
 | |
|     >>> p = polls.get_object(pk=1)
 | |
|     >>> p.add_choice(choice='Not much', votes=0)
 | |
|     Not much
 | |
|     >>> p.add_choice(choice='The sky', votes=0)
 | |
|     The sky
 | |
|     >>> c = p.add_choice(choice='Just hacking again', votes=0)
 | |
| 
 | |
|     # Choice objects have API access to their related Poll objects.
 | |
|     >>> c.get_poll()
 | |
|     What's up?
 | |
| 
 | |
|     # And vice versa: Poll objects get access to Choice objects.
 | |
|     >>> p.get_choice_list()
 | |
|     [Not much, The sky, Just hacking again]
 | |
|     >>> p.get_choice_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 2005.
 | |
|     >>> choices.get_list(poll__pub_date__year=2005)
 | |
|     [Not much, The sky, Just hacking again]
 | |
| 
 | |
|     # Let's delete one of the choices. Use delete() for that.
 | |
|     >>> c = p.get_choice(choice__startswith='Just hacking')
 | |
|     >>> c.delete()
 | |
| 
 | |
| For full details on the database API, see our `Database API reference`_.
 | |
| 
 | |
| When you're comfortable with the API, read `part 2 of this tutorial`_ to get
 | |
| Django's automatic admin working.
 | |
| 
 | |
| .. _Database API reference: http://www.djangoproject.com/documentation/db_api/
 | |
| .. _part 2 of this tutorial: http://www.djangoproject.com/documentation/tutorial2/
 |