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:

We'll assume you have Django installed 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.

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 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.

(django-admin.py 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 django-admin.py from some place on your path, such as /usr/local/bin.)

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 /home/mycode.

Let's look at what startproject created:

mysite/
    __init__.py
    manage.py
    settings.py
    urls.py

These files are:

  • __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.)
  • manage.py: A command-line utility that lets you interact with this Django project in various ways.
  • settings.py: Settings/configuration for this Django project.
  • urls.py: The URL declarations for this Django project; a "table of contents" of your Django-powered site.

The development server

Let's verify this worked. Change into the 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.95 (post-magic-removal), 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!

Changing the port

By default, the 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:

python manage.py runserver 8080

Full docs for the development server are at django-admin documentation.

Database setup

Now, edit 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:

  • 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).

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.

While you're editing settings.py, take note of the 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, INSTALLED_APPS contains the following apps, all of which come with Django:

  • django.contrib.auth -- An authentication system.
  • django.contrib.contenttypes -- A framework for content types.
  • django.contrib.sessions -- A session framework.
  • 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:

python manage.py syncdb

The syncdb command looks at the INSTALLED_APPS setting and creates any necessary database tables according to the database settings in your 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.

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 INSTALLED_APPS before running syncdb. The syncdb command will only create tables for apps in INSTALLED_APPS.

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.

Projects vs. apps

project 和 app 有何不同? 一个 app 是一个 web 应用程序--比如说一个 blog 系统或者一个简单的投票程序. 一个 project 则是一个完整站点, 它是很多相关 app 的集合. 一个 project 通常包含多个 app. 一个 app 可以被多个 project 共享.

在本教程中, 我们要在 mysite project 中创建一个简单的投票 app . 通常, app 会与 project 密切关联--也就是说, 在 poll app 中会使用 mysite.polls 这样的代码. 在本教程的后面, 我们会讨论如何解除这种关联以便独立发布一个 app.

要创建 polls app, 在 mysite 目录中执行以下命令:

python manage.py startapp polls

django 会创建目录 polls, 它的结构如下:

polls/
    __init__.py
    models.py
    views.py

该目录就是 polls app 的安身之所了.

在 Django 中写一个数据库应用程序的第一步就是定义数据模型-- 本质上就是数据的设计及附加的元数据.

哲学

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.

在这个简单的 poll app 中, 我们要创建两个 model : polls 和 choices. 一个 poll 包括一个问题和一个发布日期. 一个 choicee 有两个字段: 选择项文本及投票计数.每个 choice 都与一个 poll 相关联.

这个概念由简单的 Python 类来表示, 编辑 polls/models.py 文件,让它拥有下列内容:

from django.db import models

class Poll(models.Model):
    question = models.CharField(maxlength=200)
    pub_date = models.DateTimeField('date published')

class Choice(models.Model):
    poll = models.ForeignKey(Poll)
    choice = models.CharField(maxlength=200)
    votes = models.IntegerField()

每个 model 都是 django.db.models.Model 的一个子类. 每个 model 有一堆类属性, 每个属性表示一个字段.

每个字段都是一个 models.*Field 类的实例 -- e.g., models.CharField 是一个字符字段而 models.DateTimeField 是一个日期时间字段.

每个 models.*Field 实例的名字 (e.g. questionpub_date ) 是符合机器要求的字段名. 在 Python 代码中使用这个名字, 并且数据库会使用它做为字段的名字.

通过 Field 的第一个可选参数,可以指定一个易读的字段名. Django 会在适当的地方引用这个名字, 如果没有提供这个参数, Django 就会使用字段实例的名字. 在这个例子里, 我们仅仅为 Poll.pub_date 指定了易读字段名. 对 model 的其它字段, 实例名足够使用.

某些 Field 要求必须提供某些参数. 比如 CharField, 要求你提供一个 maxlength. 该长度用于数据库及表单验证.

最后, 我们使用 models.ForeignKey 定义了一个关系. 这告诉 Django 每个 Choice 关联到一个 Poll. Django 支持所有的常见数据库关系: many-to-ones, many-to-manys 和 one-to-ones.

激活 models

Django 通过 models 得到必要的信息, 通过这些信息, Django能够:

  • 为该 app 创建数据库中的表 (CREATE TABLE 语句).
  • 创建 Python database-access API 以存取 Poll 和 Choice 对象.

不过我们首先要告诉我们的工程, polls app 已经安装.

哲学

Django apps 都是 "pluggable": 你可以在多个 project 中使用这个 app. 并且你能单独发布一个 app, 因为他们与 django 安装无关.

再次编辑 settings.py 文件, 修改 INSTALLED_APPS 设置以包括字符串 'mysite.polls'. 修改之后设置如下:

INSTALLED_APPS = (
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.sites',
    'mysite.polls'
)

现在 django 知道 mysite 包括 polls app 了. 下面我们运行命令:

python manage.py sql polls

你会看到以下文本 (用于 polls app 的 CREATE TABLE 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;

注意以下事项:

  • 通过组合 app 的名字及小写的 model 名字, 自动生成 table 的名字.(可以改变这种默认行为.)
  • 自动添加主键. (也可以改变这种默认行为.)
  • 作为惯例, Django 给外键字段名添加了一个 "_id", 当然你也可以改变这种默认行为.
  • 外键关系显式的由 REFERENCES 语句指定.
  • 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 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:

  • python manage.py sqlinitialdata polls -- Outputs any initial data required for Django's admin framework and your models.
  • python manage.py sqlclear polls -- Outputs the necessary DROP TABLE statements for this app, according to which tables already exist in your database (if any).
  • python manage.py sqlindexes polls -- Outputs the CREATE INDEX statements for this app.
  • python manage.py sqlall polls -- A combination of all the SQL from the 'sql', 'sqlinitialdata', and 'sqlindexes' commands.

Looking at the output of those commands can help you understand what's actually happening under the hood.

Now, run syncdb again to create those model tables in your database:

python manage.py syncdb

The syncdb command runs the sql from 'sqlall' on your database for all apps in 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. syncdb can be called as often as you like, and it will only ever create the tables that don't exist.

Read the django-admin.py documentation 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:

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 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.

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 django-admin.py documentation.

Once you're in the shell, explore the database API:

# Import the model classes we just wrote.
>>> from mysite.polls.models import Poll, Choice

# No polls are in the system yet.
>>> Poll.objects.all()
[]

# Create a new Poll.
>>> from datetime import datetime
>>> p = 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. 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(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()

# 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 __str__() method to both Poll and Choice:

class Poll(models.Model):
    # ...
    def __str__(self):
        return self.question

class Choice(models.Model):
    # ...
    def __str__(self):
        return self.choice

为你的 model 添加 __str__() 方法是很重要的, 不但自己用起来方便, Django 自带的管理站点 admin 也会在适当的时候使用这个方法.

这些都是普通的 Python 方法. 下面我们来演示一下添加一个自定义方法:

import datetime
# ...
class Poll(models.Model):
    # ...
    def was_published_today(self):
        return self.pub_date.date() == datetime.date.today()

注意 import datetime 引用了 Python 模块 datetime.

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 __str__() 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 2005. Of course, if you're going through this
# tutorial in another year, change as appropriate.
>>> Poll.objects.get(pub_date__year=2005)
<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

# 给这个民意测验添加两个选择. 生成一个新的 choice  对象, 并执行 INSERT 语句
# 并返回一个新的 Choice 对象
>>> 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

# API 自动遵循你需要的关系
# 使用双下划线来分开关系
# 支持没有层次深度限制
# 找出2005年发布的所有民意测试的所有选择
>>> Choice.objects.filter(poll__pub_date__year=2005)
[<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]

# 删除一个选择. 使用 delete() 方法.
>>> c = p.choice_set.filter(choice__startswith='Just hacking')
>>> c.delete()

要了解数据库 API 的全部细节, 参阅 Database API reference.

当你已经能够对这些 API 驾轻就熟时, 阅读 part 2 of this tutorial 并使 Django 的自动管理站点 admin 工作.