手写第一个Django应用, 第二部分
我们本节开始配置数据库,创建你的第一个model,并且快速了解下Django的自动生成的 admin (后台管理)页面.
数据库配置
打开 mysite/settings.py
, 这是一个普通的python模块, 包含了Django配置的模块级变量。
默认情况下, Django 用的时 SQLite, 如果你是数据库新手, 或者仅仅想试一下Django. 这是最简单的选择, python 自带了SQLite模块, 所以无需再装其他东西, 当你真正想写一个项目时,你可能需要用到功能更丰富的数据库,比如说PostgreSQL,避免后面面临数据库切换的头疼问题.
如果你想用其他的数据库,请先安装好相应的数据库软件 ,并修改settings文件中DATABASES中 'default' 键的值,用于连接你的数据库:
- ENGINE
可以是’django.db.backends.sqlite3’或者’django.db.backends.postgresql’,’django.db.backends.mysql’, or ’django.db.backends.oracle’,当然 其它可用的也行. - NAME
数据库的名字。如果你使用的是默认的SQLite,那么数据库将作为一个文件将存放在你的本地机器内,NAME应该是这个文件的完整绝对路径,包括文件名。设置中的默认值os.path.join(BASE_DIR, ’db.sqlite3’),将把该文件储存在你的项目目录下。
如果你不是用SQLite,那你需要配置其他的设置,如 USER
, PASSWORD
, and HOST
都是必需的. 详细请看 DATABASES
相关.
SQLite外的其他数据库:
在使用非SQLite的数据库时,请务必首先在数据库提示符交互模式下创建数据库,你可以使用命令:“CREATE DATABASE database_name;” 确保你在settings文件中提供的数据库用户具有创建数据库表的权限,因为在接下来的教程中,我们需要自动创建一个test数据库。 如果你使用的是SQLite,那么你无需做任何预先配置,数据库文件会自动创建
在修改settings文件时,请顺便将TIME_ZONE设置为你所在的时区。(天朝: TIME_ZONE = 'UTC')
同时,请注意settings文件中顶部的INSTALLED_APPS设置项。它保存了所有的在当前项目中被激活的Django应用。你必须将你自定义的app注册在这里。每个应用可以被多个项目使用,而且你可以打包和分发给其他人在他们的项目中使用。
默认情况,INSTALLED_APPS 中包含以下应用,它们都是Django自带的:
-
django.contrib.admin
– admin站点, 你很快会用到. -
django.contrib.auth
– 身份认证系统. -
django.contrib.contenttypes
– 内容类型框架. -
django.contrib.sessions
– sessions 框架. -
django.contrib.messages
– 消息框架. -
django.contrib.staticfiles
– 静态文件管理框架.
以上应用默认会包含,对于大多数项目都是需要的.
上面的每个应用都至少需要使用一个数据库表,所以在使用它们之前我们需要在数据库中创建这些表。使用这个命令:
python manage.py migrate
migrate命令将根据INSTALLED_APPS、mysite/settings.py的数据库设置以及app的数据库迁移(稍后会讲到)信息创建相关的表 ,你将看到每一个数据库消息的信息。如果你感兴趣,可以在你的数据库命令行下输入:\dt
(PostgreSQL), SHOW TABLES;
(MySQL), 或 .schema
(SQLite), 或SELECT TABLE_NAME FROM USER_TABLES;
(Oracle) 来列出 Django 所创建的表。
关于简化
就像我们上面说的,默认情况下会自带那些公共应用,但不是每一个应用都需要这么多。如果你不需要其中一个或所有的,你可以在migrate前,将INSTALLED_APPS内注释掉或者删除对应的行。 migrate命令只会针对INSTALL_APPS进行数据库迁移.
创建Model
现在我们开始定义models - 本质上,就是你的数据库结构,和其他的一些元数据信息.
哲(♂)学
model就是你唯一明确的真实数据源。Django 遵循 DRY Principle.目的是让你可以在一个地方定义你的数据模型, 从中生成数据.
在我们的投票应用中,我们将创建两个模型:Question and Choice。Question表包含问题和发布日期两个字段。Choice表有两个字段:选择文本和投票计数。每一条choice都关联到一条Question。
这些概念都由python的类来定义。编辑polls/models.py,如下:
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, on_delete=models.CASCADE)
choice_text = models.CharField(max_length=200)
votes = models.IntegerField(default=0)
以上代码简单明了。每一个model都由一个类来定义,该类是django.db.models.Model
的子类。每个类都由数个变量组成,分别对应了数据库里的字段。
每一个字段都是Field
类的实例 -- 如 CharField
是字符串字段,DateTimeField
是时间字段。这告诉Django每个字段用来存储什么类型的数据。
每个Field
的实例名(如question_text 或 pub_date)就是字段的名字名,一个机器友好型的名字。在代码里你会用到它,而数据库里用它来作字段名。
每个Filed
里的第一个可选的位置参数,常常被用来指定一个可读性好的名字。这在Django的一些内部机制中有所用处,也可以兼做文档。如果没有指定这个字段,Django会默认使用机器可读的名字。在这个例子中,我们只为pub_data定义了友好的名字。model的其他字段已经通俗易懂。
有一些字段有必选参数。例如,CharField要求你给它一个max_length。这个参数不仅在建表时用到,在数据验证中也会用到,我们稍后会看到。
Field 还具有各种可选位置参数。在这个例子中,我们设置votes字段的default
参数为0。
最后,注意我们使用ForeignKey
定义了一个外键关联。它告诉Django每个Choice都只关联一个Question。Django支持所有常见的数据库关联:多对一、多对多和一对一。
激活 model
这简短的model代码给了Django许多信息。有了它,Django就可以:
- 为该应用创建数据库表(CREATE TABLE 语句)。
- 创建一个访问数据库的 python API 来访问Question对象和Choice对象。
但是,我们首先得告诉项目:polls应用已经安装。
哲学
Django 应用是“插件化”的,即可以在多个项目中使用同一个应用,也可以分发这些应用, 因为它们不需要与某个特定的Django安装绑定。
为了将App整合进项目里,我们需要在 settings.py 的 INSTALLED_APPS
里添加该 App 的引用,PollsConfig 类在 polls/apps.py 文件中,所以按照点来分割路径就是polls.apps.PollsConfig
,编辑mysite/settings.py文件,在 INSTALLED_APPS
中添加按点分割的路径,如下:
mysite/settings.py
INSTALLED_APPS = [
'polls.apps.PollsConfig',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
现在Django就知道他整合了polls应用。我们输入以下命令:
python manage.py makemigrations polls
你可以看到类似以下的输出:
Migrations for 'polls':
polls/migrations/0001_initial.py:
- Create model Choice
- Create model Question
- Add field question to choice
通过运行makemigrations
,你将告诉Django你已经对你的models做了一些修改(在本例中,你创建了新的model类),这些修改会作为migration记录来存储。
Migrations 就是Django存储的models修改记录(也就是数据库表) - 他们只是在磁盘的文件, 你可以查看刚刚创建新model时产生的migration, 他们在polls/migrations/0001_initial.py
文件里, 别担心, 你无需每次都去看一下, 但是他们是可编辑的, 以便有时候你想修改他们.
There’s a command that will run the migrations for you and manage your database schema automatically - that’s called migrate
, and we’ll come to it in a moment - but first, let’s see what SQL that migration would run. The sqlmigrate
command takes migration names and returns their SQL:
有一个命令可以运行这些migrations文件来自动管理你的数据库表, -- 它就是migrate
, 我们待会儿会用到它 -- 但首先我们看一下,那些migration会执行哪些sql语句。sqlmigrate
命令接收migration文件名,并返回sql语句:
python manage.py sqlmigrate polls 0001
你将看到类似以下输出(我们重新排版了下)
BEGIN;
--
-- Create model Choice
--
CREATE TABLE "polls_choice" (
"id" serial NOT NULL PRIMARY KEY,
"choice_text" varchar(200) NOT NULL,
"votes" integer NOT NULL
);
--
-- Create model Question
--
CREATE TABLE "polls_question" (
"id" serial NOT NULL PRIMARY KEY,
"question_text" varchar(200) NOT NULL,
"pub_date" timestamp with time zone NOT NULL
);
--
-- Add field question to choice
--
ALTER TABLE "polls_choice" ADD COLUMN "question_id" integer NOT NULL;
ALTER TABLE "polls_choice" ALTER COLUMN "question_id" DROP DEFAULT;
CREATE INDEX "polls_choice_7aa0f6ee" ON "polls_choice" ("question_id");
ALTER TABLE "polls_choice"
ADD CONSTRAINT "polls_choice_question_id_246c99a640fbbd72_fk_polls_question_id"
FOREIGN KEY ("question_id")
REFERENCES "polls_question" ("id")
DEFERRABLE INITIALLY DEFERRED;
COMMIT;
请注意以下几点:
- 输出的具体内容会依据你使用的数据库而不同。 以上例子使用的数据库是PostgreSQL。
- 表名是自动生成的,由app的名字(polls)和模型名字的小写字母组合而成 —— question和choice。(你可以重写这个行为。)
- 主键(IDs)是自动添加的。 (你也可以重写这个行为。)
- 按照惯例,Django会在外键的字段名后面添加 "_id"。(是的,你依然可以重写这个行为。)
- 外键关系由FOREIGN KEY约束显式声明。不用在意DEFERRABLE 部分;它只是告诉PostgreSQL直到事务的最后再执行外键关联。
- 这些SQL语句是针对你所使用的数据库定制的,所以会为你自动处理某些数据库所特有的字段例如auto_increment(MySQL)、 serial (PostgreSQL)或integer primary key autoincrement (SQLite) 。在处理字段名的引号时也是如此 —— 例如,使用双引号还是单引号。
-
sqlmigrate
命令并不会在你的数据库上真正运行迁移文件 —— 它只是把Django 认为需要的SQL打印在屏幕上以让你能够看到。 这对于检查Django将要进行的数据库操作或者你的数据库管理员需要这些SQL脚本是非常有用的。
如果有兴趣,你还可以运行python manage.py check
, 它会检查你的项目中的模型是否存在问题,而不用执行迁移或者操作数据库
现在,再次运行 migrate
, 在你的数据库中创建model所对应的表:
$ python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, contenttypes, polls, sessions
Running migrations:
Rendering model states... DONE
Applying polls.0001_initial... OK
migrate
命令将找到所有尚未被应用的迁移文件(Django有专门的一张表 - django_migrations
, 来追踪哪些迁移文件已经被应用过),并且在你的数据库上运行它们 —— 本质上来讲,就是同步你对models的修改到数据库表中。)
Migrations 功能非常强大,在你开发项目的过程中,当你在更改models后,无需删除数据库或表再重建。它专注于热升级你的数据库且不丢数据。我们将在本教程的后续章节中详细介绍,但是现在,请记住模型变更的三个基本步骤:
- 修改你的模型(在models.py文件中)。
- 运行
python manage.py makemigrations
,为这些修改创建迁移文件 - 运行
python manage.py migrate
,将这些改变更新到数据库中。
将生成和应用Migrations命令分开, 是因为你可能需要将迁移文件提交到你的版本控制器并跟随你的应用, 这样做不仅使开发变得更加简单,而且对其他开发者以及线上环境非常有用。
阅读 django-admin documentation 来详细了解manage.py
工具所能做的事情。
玩玩API
现在,我们切换到python shell的交互模式,感受一下Django给的API。用如下命令来调出python shell:
$ python manage.py shell
我们使用这个(python manage.py shell
)而不是直接进入python交互环境(python
), 因为manage.py
设置了DJANGO_SETTINGS_MODULE
环境变量. 它告诉Django导入了mysite/settings.py模块.
一旦你进入了这个python shell, 那我们就开始探索这些database API吧:
>>> from polls.models import Choice, Question # 导入我们刚刚创建的model类.
# 目前还没有questions对象
>>> Question.objects.all()
<QuerySet []>
# 创建一个Questions对象
# 默认的设置文件中是支持时区的,
# 所以Django会通过 tzinfo 模块给 pub_date 一个datetime.
# 使用timezone.now(), 它会替代datetime.datetime.now()完成时间设置工作.
>>> from django.utils import timezone
>>> q = Question(question_text="What's new?", pub_date=timezone.now())
# 调用 save() 来将对象保存进数据库
>>> q.save()
# 现在他有一个id了
>>> q.id
1
# 通过访问Python属性的方式来访问model字段的值
>>> q.question_text
"What's new?"
>>> q.pub_date
datetime.datetime(2012, 2, 26, 13, 0, 0, 775217, tzinfo=<UTC>)
# 通过改变对象属性的方式来改变字段的值, 改变后需要调用save()才能生效
>>> q.question_text = "What's up?"
>>> q.save()
# objects.all() 显示所有在数据库中的questions对象
>>> Question.objects.all()
<QuerySet [<Question: Question object (1)>]>
秋德马嘚, <Question: Question object (1)>
是什么鬼? 这可不是一种友好的对象表示方式, 让我们编辑Question
模型(在 polls/models.py
文件中)来完善他, 对Question
and Choice
类都添加__str__()
方法:
polls/models.py
from django.db import models
class Question(models.Model):
# ...
def __str__(self):
return self.question_text
class Choice(models.Model):
# ...
def __str__(self):
return self.choice_text
给你的模型类添加__str__()
方法很重要,不仅会使你自己在使用交互式命令行时看得更加方便,而且会在Django自动生成的管理界面中对模型对象使用这种表示。
这是非常普通的Python方法。现在让我们来演示一下如何添加自定义方法:
polls/models.py
import datetime
from django.db import models
from django.utils import timezone
class Question(models.Model):
# ...
def was_published_recently(self):
return self.pub_date >= timezone.now() - datetime.timedelta(days=1)
注意import datetime
和from django.utils import timezone
分别引用Python 的标准datetime
模块和 Django的django.utils.timezone
中时区相关的工具, 如果你不了解Python中时区的处理方法,你可以在time zone support docs中了解更多的知识。
保存这些改动,然后通过python manage.py shell
另外打开一个新的Python 交互式shell:
>>> from polls.models import Choice, Question
# 确保我们添加的__str__方法生效了
>>> Question.objects.all()
<QuerySet [<Question: What's up?>]>
# Django提供大量的数据库查询API, 通过关键字参数的方式来调用.
>>> Question.objects.filter(id=1)
<QuerySet [<Question: What's up?>]>
>>> Question.objects.filter(question_text__startswith='What')
<QuerySet [<Question: What's up?>]>
# 得到今年发布的question对象
>>> from django.utils import timezone
>>> current_year = timezone.now().year
>>> Question.objects.get(pub_date__year=current_year)
<Question: What's up?>
# 当请求的ID不存在时, 会抛出异常
>>> Question.objects.get(id=2)
Traceback (most recent call last):
...
DoesNotExist: Question matching query does not exist.
# 大多数情况下都是通过主键进行查询, 所以Django提供一个关于主键查询的快捷方式
# 以下与Question.objects.get(id=1)是一样的.
>>> Question.objects.get(pk=1)
<Question: What's up?>
# 确保我们自定义方法生效
>>> q = Question.objects.get(pk=1)
>>> q.was_published_recently()
True
# 每个question对象都有多个choice对象(一对多), 调用 create 来构造一个新的 choice 对象,
# 它会使用 INSERT 语句, 添加choice对象的一系列字段, 然后返回一个choice对象,
# django通过API来创建一个含有"反向关联"关系的外键集合.首先, 我们要获取指定的question对象
>>> q = Question.objects.get(pk=1)
# 显示所有与choices对象有关系的对象集合 -- 目前没有
>>> q.choice_set.all()
<QuerySet []>
# 创建3个choice对象
# (译者注: choice类中定义了question的外键, 所以question可以通过`类名_set`来反向关联,
# 也可以通过定义外键时设置的related_name参数的值来进行反向关联)
>>> q.choice_set.create(choice_text='Not much', votes=0)
<Choice: Not much>
>>> q.choice_set.create(choice_text='The sky', votes=0)
<Choice: The sky>
>>> c = q.choice_set.create(choice_text='Just hacking again', votes=0)
# Choice 对象有API来访问他们关联的 Question 对象
>>> c.question
<Question: What's up?>
# 反之亦然: Question对象也可以访问Choice对象.
>>> q.choice_set.all()
<QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]>
>>> q.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.
# 我们可以使用双下划线来调用API自动的处理关系, 其中关系的深度没有限制.(这段感觉没翻译好, 原文在)
# 如下, 找到所有 pub_date 为今年, 并且可以是属于任何Question的Choice(这里重用了我们在上面创建的`current_year`变量.
>>> Choice.objects.filter(question__pub_date__year=current_year)
<QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]>
# 我们可以使用delete()来删除这些choices QuerySet中的一个.
>>> c = q.choice_set.filter(choice_text__startswith='Just hacking')
>>> c.delete()
更多关于模型相关的信息,请查看 Accessing related objects。更多关于如何在这些API中使用双下划线来执行字段查询的信息,请查看 Field lookups.。想了解所有数据库API的信息,请查看我们的 Database API reference.
介绍 Django Admin
哲♂学
为你的团队或客户编写增删改查的admin管理系统是非常乏味的,也没有多少创造性价值。基于这考虑,Django自动的为你的models创建了管理接口。
Django最初是为新闻站点开发的,新闻发布者和公共站点界限明显。管理员通过这个系统添加新闻、时间、体育赛事等,这些信息展示在公共网站上。Django提供了一个统一接口给站点管理员来编辑内容。
admin系统并不是为了访客设计的,只对站点管理员开放。
创建管理员
首先我们需要创建一个能登录admin站点的用户,运行以下命令:
python manage.py createsuperuser
输入你喜欢的名字,按下回车:
Username: admin
然后会提示输入你的邮箱:
Email address: admin@example.com
最后一步是输入密码。你需要输入两次,第二次作为第一次的进一步确认:
Password: **********
Password (again): *********
Superuser created successfully.
启动开发服务器
Django的admin站点默认是激活的。让我们开启开发服务器来探索一下:
如果服务器没开启,用下面的方法来启动:
python manage.py runserver
现在,打开web浏览器,本地域名访问“/admin/” -- 比如说http://127.0.0.1:8000/admin/。你可以看到admin的登录界面:
translation 默认是开启的,登录界面显示的应该是你的母语,这取决于你的浏览器设置,如果Django有这种翻译版本的话。
进入admin页面
现在,你可以用你刚刚创建的超级用户来登录了。登录后你应该可以看到如下index页面:
当前已经有两个可编辑的内容:groups和users。它们是
django.contrib.auth
模块提供的身份认证框架。
在admin中注册投票应用
但是我们创建的投票应用哪去了呢?它并没有在admin的首页里。我们还有一件事要做:告诉admin站点Question要有admin交互界面。编辑polls/admin.py文件,代码如下:
polls/admin.py
from django.contrib import admin
from .models import Question
admin.site.register(Question)
探索Admin功能
Question注册之后,Django就知道要将它展示在admin的首页了。
点击“Questions”。 现在,你会进入Question的“变更列表”。 这个界面显示了数据库中的所有question,你可以选择一个来更改它。 我们在前面创建的“What’s up?” Question对象也在这里:
点击“What’s up?” Question对象来编辑它:
注意事项:
- 这个表单是根据
Question
模型文件自动生成的。 - 模型中不同类型的字段(
DateTimeField
,CharField
)会对应相应的HTML输入控件。每一种类型的字段,Django管理站点都知道如何显示它们。 - 每个
DateTimeField
字段都会有个方便的JavaScript快捷键。Date有个“Today”的快捷键和一个弹出式日历,time栏有个“Now”的快捷键和一个列出常用时间选项的弹出式窗口。
界面的底部提供了几个按钮:
- Save —— 保存更改,并返回当前类型对象的变更列表界面。
- Save and continue editing —— 保存更改并且重新载入当前对象的管理界面。
- Save and add another —— 保存更改并且载入一个当前类型对象的新的、空白的表单。
- Delete —— 显示一个删除确认界面。
如果“Date published”的值和你在教程1中创建这个Question对象时的时间不相符,可能是因为你忘记将TIME_ZONE设置为你当地的时区。修改它,然后刷新界面,再次检查一下是否正确。
通过“Today”和“Now”这两个快捷键更改“Date published”字段。,然后点击 “Save and continue editing”。然后点击右上角的“History”按钮。 你将看到一个页面,列出了通过Django管理界面对此对象所做的全部更改的清单,包含有时间戳和修改人的姓名等信息:
当你适应了models的API,熟悉了admin站点后,请移步 part 3 of this tutorial 来学习如何给投票应用编写更多的views函数.