说明1:本文翻译自Django官方文档Writing your first Django app, part 1。
说明2:本文中,采用django 1.8以及Python 2.7,Debaian Linux;系统运行在Raspberry Pi上。
我们通过例子(example)来进行学习。
通过本教程,我们将会创建一个基本的投票应用(a basic poll application)。
它包含两部分:
·一个公共站点,可以用来浏览并接受人们的投票。
·一个后台,你可以通过它来添加、改变以及删除投票。
我们假设你已经安装了Django。如果你不确定,可以通过以下命令来确定你的系统上是否已经安装Django以及Django的版本。
注1:原文中使用的为Python 3.X,本文使用Python 2.7。因为个别代码或命令会略有不同。
$ python -c "import django; print django.get_version()"
如果系统中已经安装了Django,你应该可以得到系统中安装的Django的版本,如图1所示;如果系统中没有安装Django,系统将会显示一个错误(error):"No module named django(没有django模块)"。
本教程基于Django 1.8。如果你的Django版本与本教程不匹配,请升级至最新的Django 1.8。关于卸载旧版并安装新版Django的教程可参考该教程“How to install Django"。
创建一个工程(Creating a project)
如果这是第一次使用Django,你需要注意一些项目初始化的步骤。也就是说,你需要通过一些Django命令来自动生成一些初始代码来建立一个Django工程。这些初始化代码主要包含了一些Django实例(instance of Django)的设置:数据库设置、Django相关的设置、应用相关的设置。
在命令行下,用cd命令进入存储代码的目录,并执行下列命令:
$ django-admin.py startproject mysite
或者
$django-admin startproject mysite
这个命令将在你的当前目录下创建一个名为mysite的文件夹。如果该命令不能正确执行,请参考“Problems running django-admin"。
注2:关于你所创建的Django Project的命名问题:请不要用Python或Django的内建变量来进行project命名。也就是说,你需要避免使用类似Django或者test之类的名字来命名你的Django project。
注3:关于django代码的存放:区别于PHP,可以将代码存放在任意你希望的位置,而不需放置在www/文件夹下。
下面我们来看看startproject命令创建了什么:
mysite/ 项目的根目录,是放project的容器。其命名和Django无关,可以随意修改该文件夹名
manage.py 命令行工具-可以通过它与Django project进行互动。参考
mysite/ Django project实际的Python代码包(Python package),目录名称不能改变。
_init_.py 空文件-通知Python该目录下文件为一个Python Package。
settings.py Django Project的设置文件。参考
urls.py Django Project的URL声明文件,类似基于Django的站点的“目录”。参考
wsgi.py 一个对于WSGI兼容的Web服务器的入口点。参考
数据库设置(database setup)
现在我们来编辑mysite/setting.py。该文件是一个普通的Python模块(Python module)并使用模块级(module-level)的变量来表示Django设置。
Django项目默认使用SQLite数据库。如果你对数据库并不熟悉或者只是想试一下django,那么使用SQLite是一个最简单的选择。SQLite包含在Python中,因此你不需要安装其他软件来支持你的数据库。然而,当你开始开发一个真正的Django项目时,你可能需要一个类似PostgreSQL的强健数据库。
如果你希望使用其他数据库,需要安装合适的数据库绑定(database bindings),并在DATABASES 'default'项下修改一下键值(keys)以适合新的数据库连接的需要,如图2所示:
·ENGINE-可以为'django.db.backends.sqlite3', 'django.db.backends.postgresql_psycopg2', 'django.db.backends.mysql', 'django.db.backends.oracle'; 其他数据库设置参考
·NAME-数据库名称。如果使用SQLite,数据库就是电脑上的一个文件。这种情况下,NAME应该为这个数据库文件的完整绝对路径(包含文件名)。其默认值为os.path.join(BASE_DIR,'db.sqlite3'),将把数据库文件存储在Django project的目录下。
如果没有使用SQLite作为数据库,那么还需要设置USER,PASSWORD以及HOST。更多的细节可以参考相关文档。
注4:如果使用PostgreSQL或者MySQL,需要在设置数据库之前首先建立一个数据库。一般情况下,可以在数据库的交互命令行下,使用"CREATE DATABASE database_name;"来实现。如果使用SQLite则在设置数据库之前不需要做任何事情,数据库文件将会在需要的时候自动生成。
在编辑mysite/setting.py的时候,将TIME_ZONE设置为你的站点所在的时区。
另外,需要注意setting.py文件顶端的INSTALLED_APPS设置。这里包含了这个Django实例(Django instance)中所有激活的Django应用(Django applications)的名称。Django应用可以被多个Django项目(Django project)所使用,也可以将其打包并分发给其他人的project使用。
默认情况下,INSTALLED_APPS包含了以下应用,其全部来自Django:
·django.contrib.admin-管理台站点。我们将在本教程的第二部分使用它。
·django.contrib.auth-一套认证(authentication)系统。
·django.contrib.contenttypes-一个内容类型(content types)框架。
·django.contrib.sessions-一个会话(session)框架。
·django.contrib.messages-一个消息(messageing)框架。
·django.contrib.staticfiles-一个管理静态文件(static files)的框架。
以上应用默认安装,以方便一般情况下的使用。
部分以上应用的运行至少需要用到一张数据库表(database table)。因为我们在使用以上应用之前需要在数据库中创建数据表(tables),可以使用以命令实现:
$ python manage.py migrate
migrate命令会根据mysite/setting.py中的数据库设置创建任何在INSTALLED_APPS中需要使用的数据库表(database tables),并且数据库迁移(database migrations) 会与app附带绑定(shipped with)在一起(我们将会在后面提到)。在每次迁移(migration)执行时,你都会收到消息,如图3所示。
如果你感兴趣的话,在你数据库的命令行里执行以下命令以显示Django创建的数据表:
\dt (PostgreSQL)
SHOW TABLES; (MySQL)
.schema (SQLite)
注5:正如我们前面所提到的,这些默认应用是对一般或者大多数情况而言的,并非每个项目都需要它们。如果你并不需要这些默认应用中的一部分或者全部,那么可以在执行migrate命令前将其注释掉或者在INSTALLED_APPS中直接删除相应的代码行。migrate命令指挥对INSTALLED_APPS中的应用执行数据迁移(migration)。
开发服务器(The development server)
我们来确认一下Django是否工作正常。在外层mysite/目录下(project的根目录)执行一下命令:
$ python manage.py runserver
你将在命令行中看到一下输出:
可以看到,你已经开启了Django的开发服务器。这是一个轻量级的、纯粹使用Python编写的web服务器。该web服务器包含在Django中,这样开发者可以使用它进行快速开发,而不需要去在真实部署之前花费精力去设置生产坏境下的web服务器,比如Apache.
注6:请不要再正式生产环境下使用该web服务器,其仅适用于开发使用。
现在web服务器开始运行了,使用你的浏览器访问http://127.0.0.1:8000/。你可以看到一个柔和的浅蓝色的“Welcome to Django”页面,如图5所示。
注7:默认情况下,runserver命令会在内部ip的8000端口上开启开发服务器。如果需要修改服务器端口,可以将端口号作为参数传递进命令行,比如我们在8080端口上开启开发服务器:
$ python manage.py runserver 8080
如果需要修改服务器的ip地址,可以将ip地址连同端口号一起传递。因此,如果需要监听所有公共ip地址(可以在其他电脑上展示你的作品)可以执行一下命令:
$ python manage.py runserver 0.0.0.0:8000
关于开发服务器的全部文档可以在runserver参考文档中找到。
注8:开发服务器会对每一个请求(request)自动重载(reload)Python代码。因此,当代码修改后并不需要手动重启开发服务器。然而,有些操作(比如添加文件)并不会触发服务器重启,因此在这些情况下需要手动重启服务器。
创建模型(Creating models)
现在我们的项目环境已经搭建完毕,可以开始工作了。
每一个Django应用都包含有一套特定规制格式的Python代码包(Python package).Django会自动生成一个应用的基本目录结构,因此你只需关注代码编写而不必为如何创建应用的文件目录而分心。
注9:关于Django项目(Projects)和Django应用 (apps)之间的区别。所谓app是指完成一些功能的web应用,比如博客系统(weblog system),公共记录的数据库(a database of public records)或者是一个简单的投票系统(a simple poll app)。一个Django项目(project)是指一个特定网站的一系列配置文件和应用的集合。一个项目(project)可以包含多个应用(app),一个应用(app)可以被多个项目(project)使用。
Django应用的代码可以存放在Python路径(Python path)上的任意位置。在本教程中,我们将在manage.py同目录下创建poll应用。这样做的好处是该应用可以作为该项目的顶级模块被导入,如不需要作为mysite的子模块。
因此,在创建该应用的之前,请首先确认处在与manage.py同一目录下,并执行以下命令:
$ python manage.py startapp polls
该命令会创建polls目录,该目录结构为:
polls/
_init_.py
admin.py
migrations/
_init_.py
models.py
tests.py
views.py
以上目录结构即为投票应用(poll application)的目录结构。
使用Django编写一个基于数据库的web应用的第一步是定义模型(models),其中主要包括:你的数据库结构(database layout)以及其他元数据(metadata)。
在我们这个简单的poll应用中,我们将创建两个模型(models):Question和Choice。其中Question中包含问题(question)和发布时间(a publication date)。Choice中包含两个字段(fields):选择的内容(the text of the choice)以及票数计算(a vote tally)。其中每一个Choice与一个Question相关联。
以上的概念通过简单的Python类(python classes)来实现。编辑polls/models.py文件:
from django.db import models
class Question(models.Model):
question_text=models.CharField(max_length=200)
pub_date=models.DateTimeField('date published')
class Choice(models.Model):
question=models.ForeignKey(Question)
choice_text=models.CharField(max_length=200)
votes=models.IntegerField(default=0)
上述代码非常简单直接。每个模型(model)都通过一个django.db.models.Model的子类来表示。每个模型(model)都包含一系列的类变量(class variables),每个变量都代表了一个模型(model)中的数据库字段(database field)。
每个字段(field)都通过一个字段类(a Field class)的实例来表示,比如:CharField表示字符字段,DateTimeField表示时间字段。这些内容用来向Django说明每个字段(field)中所包含的数据类型。
每一个Field实例的命名(比如question_text 或者 pub_date)就是字段(field)的名称,这是一种易于机器识别的格式(a machine-friendly format)。你可以在Python代码中使用这些名称,数据库也会将其作为列名(column name)。
你也可以是使用Field类可选的第一个位置参数(positional argument)来指定一个易于理解的名称。这一般用于django的內省部分(introspective parts)并可以同时作为文档使用。如果这一字段没有提供,django将自动采用一个机器可读(machine-readable)的名称。在本例中,我们为Question.pub_date定义了人类易读(human-readable) 的名称。对于该模型中的其他字段,其字段机器易读(machine-readable)的名称同时也是人类易读(human-readable)的。
一些Field类需要传递一些必要参数(required argument),以CharField类为例,需要被传递一个max_length参数。这些不仅用于数据库架构(database schema)也同时可以用于模式(model)的激活生效。
一个Field类也可以同时具有很多可选参数,比如在本例中将votes的default值设置为0。
最后,我们使用ForeignKey来进行关系定义。这可以通知Django每个Choice只与一个Question相关联。Django支持所有常见的数据库关系:多对一,一对一以及多对多。
激活模型(Activating models)
我们在前面polls/models.py中编写的那一点儿代码可以传递给Django很多信息。通过这些代码,Django可以:
·为该应用创建一套数据库架构(database schema)(类似于CREATE TABLE命令)
·为接入(accessing)Question和Choice对象创建一个Python数据库接入API(Python database-access API)。
但是,首先我们需要告知我们Django项目(project),我们已经安装了polls应用。
注10:Django是“可插拔”的:你可以在多个项目中使用一个应用,也可以分发应用而并不需要和一个给定的Django安装绑定在一起。
编辑mysite/settings.py,并修改INSTALED_APPS设置使其包含'polls'字符串,如图6所示:
现在,Django已经知道我们安装了'polls'应用。下面执行以下命令:
$ python manage.py makemigrations polls
应该可以得到以下显示,如图7所示:
通过执行makemigrations命令,你告知Django你已经对你的模型(models)做了一些修改(在本例中我们创建了新的模型)并且希望这些修改被存储下来(you'd like the changes to be stored as a migration)。
迁移(Migrations)是Django存储模(models)型修正以及你的数据库架构(database schema)的方式-其实它们只是存储在硬盘上的文件。如果你喜欢的话,你可以阅读你的新模型的迁移,就是polls/migrations/0001_initial.py文件。
migrate命令可以自动的执行数据迁移(migrations)并管理你的数据库架构(database schema),我们将在后文中提到。首先,我们来看一下数据迁移(migration)如何执行SQL语句。其中sqlmigrate命令以migration名称为参数并返回SQL语句。
$ python manage.py sqlmigrate polls 0001
你应该可以看到以下信息,如图8所示:
下面给出一个可读的格式,如图9所示:
注11:sqlmigrate命令其实并没有对你的数据库执行数据迁移(migration)。它只是在屏幕上显示Django将会执行什么SQL命令。
如果有兴趣的话,你可以执行 python manage.py check命令;这一命令会在不执行数据迁移(migration)和不改动数据库的情况下检测你的Django项目中的任何问题。
现在,我们再次执行migrate命令以便在数据库中创建那些模式数据表(those model tables):
$ python manage.py migrate
会显示如下信息,如图10所示:
migrate命令会执行所有未执行的数据迁移(migration)(django通过数据库中一个名为django_migrations的特殊数据表来记录数据迁移执行与否)并在你的数据库架构上同步你所对模型(models)所做的修改。
数据迁移功能十分强大并且可以超时(over time)修改你的模型(models)。当你开发你的django项目时,你并不需要删除你的数据库或者数据表并建立新的数据库或数据表。它可以在不丢失数据的情况下实时(live)的升级你的数据库。我们会在该教程后面的章节中进一步阐述这一点。现在,只需记住执行模型修改(making model changes)的三个步骤:
·修改你的模型(在models.py中)
·执行python manage.py makemigrations来创建这些修正的数据迁移(migration)
·执行python manage.py migrate 将这些改变应用到数据库中
分别使用两条不同的命令来创建(makemigrations)和应用(migrate)数据迁移(migration)是因为你需要提交(commit)数据迁移(migration)到版本控制系统(version control system)并将其与app附加在一起;这些设定更加易于开发者进行开发,并且在生产环境中也更加易用。
对于manage.py的更多功能参考“django-admin 文档”。
使用API(Playing with the API)
现在我们进入Python的交互shell界面并操作Django API。我们可以使用一下命令进入Python shell:
$ python manage.py shell
如图11所示:
我们使用上述命令取代简单的输入“python”,是因为manage.py将DJANGO_SETTINGS_MODULE设置为环境变量,使Django可以将Python输入路径(Python import path)传递到mysite/settings.py文件中。
下面我们来看一下Django的数据库API (database API):
首先输入我们前面写的polls/models.py中的Question 和 Choice 类。
from polls.models import Question, Choice #import the model classes we just wrote
可以看到系统中目前并没有问题(question)存在,如图12所示:
下面,我们来添加一个新的问题,执行如下操作:
from django.utils import timezone
q=Question(question_text="What's new ?",pub_time=timezone.now())
注12:在Django项目的默认设置文件中是可以支持时区功能的。在这里我们希望问题的发布时间(pub_time)是一个与时区相关的时间信息。因此我们使用timezone.now()取代了datetime.datetime.now()。
下面将我们新创建的问题对象保存入数据库,执行以下命令:
q.save()
执行过数据保存命令save()后,我们的新建对象q应该在数据库中具有个一个ID,可通过一下命令查看:
q.id
结果如图13所示:
注13:根据后台使用的数据库的不同,结果可能显示1亦可能显示1L。本例中使用的数据库为SQLite。
我们还可以通过python命令查看模型字段中的其他值(access model field values):
q.question_text
q.pub_date
结果如图14所示:
也可以在shell中改变相关参数的值,同时不要忘记保存进数据库:
q.question_text="what is up?"
q.save()
结果如图15所示:
下面我们再次通过objects.all()来查看数据库中所有已存在的问题(question),执行下列命令:
Question.objects.all()
得到如下结果如图16:
可以看到Question.objects.all()命令返回的信息对我们的帮助不大。我们可以通过修改polls/models.py中的Question和Choice模型来解决这个问题。具体说来就是在Question和Choice类中添加__unicode__函数,如图17所示:
注14:本文中采用python 2.7,如果使用Python 3,请使用__str__()函数,具体信息可参考英文原文。
在models.py中添加__unicode__()不仅可以在交互shell界面中为程序员提供方便的信息,也在Django自动生成的后台管理时具有重要作用。
重新开启一个Python shell,确认我们的修改时候生效,如图18所示:
可以看到,我们在polls/models.py中的修改已经生效。接下来,我们继续改进polls/models.py,如图19所示:
关于datetime模块、django.utils.timezone模块想参考官方文档中的相关资料。关于Django中与时区处理相关的内容也可以参考time zone support doc。
Django提供了丰富的关键字参数(keyword arguments)驱动的数据库查看API(a rich database lookup API)。比如,可以执行以下命令:
Question.objects.filter(id=1)
Question.objects.filter(question_text__startswith='what')
结果如图20 所示:
我们还可以请求(get)今年发布的问题,执行以下命令:
from django.utils import timezone
current_year=timezone.now().year
Question.objects.get(pub_date__year=current_year)
结果如图21所示:
当你请求(get)一个并不存在的ID时,会得到一个异常,如图22:
通过主键(primary key)进行查询是最常见的形式,Django也提供了利用主键进行请求(get)的方法:
Question.objects.get(pk=1)
在这里,该命令与Question.objects.get(id=1)等价,其执行结果如图23:
接下来,我们来确认在图19中所做的改进生效与否:
q=Question.objects.get(pk=1)
q.was_published_recently()
执行结果如图24所示:
我们来关注Choice模型,并将会为问题(Question)提供一系列的选择(Choice),Question和Choice通过外键(foreign key)建立关联。首先执行以下命令:
q=Question.objects.get(pk=1)
q.choice_set.all()
结果如图25所示,选择集显示为空(Display any choices from the related object set --none so far):
我们新建三个选择,分别执行以下命令:
q.choice_set.create(choice_text='Not much',votes=0)
q.choice_set.create(choice_text='The sky',votes=0)
c=q.choice_set.create(choice_text='Just hacking again',votes=0)
结果,如图26所示:
Choice对象也可以通过API连接(access)到相关的Question对象。执行下列命令:
c.question
结果如图27:
反之亦然,如前所述,由Question对象也可以通过API连接到Choice对象,执行命令:
q.choice_set.all()
q.choice_set.count()
结果如图28:
同时,要注意到双下划线的使用,我们在前文已经使用过,如下例:
Choice.objects.filter(question__pub_date__year=current_year)
运行结果如图29:
我们也可以使用delete()执行删除操作:
c=q.choice_set.filter(choice_text__startswith='Just hacking')
c.delete()
关于模型关系(model relations)的更多信息,可以参考“Accessing related objects”。关于API中双下划线的使用,可以参考“Field lookups”。关于数据库API的详细信息,可以参考“Database API reference”。