Part 2

编写你的第一个Django应用程序,第2部分

数据库设置

现在,打开 mysite/settings.py 。它是一个用模块级别变量来表示 Django 配置的普通 Python 模块。

默认情况下,该配置使用SQLite。如果你是数据库初学者,或者你只是想要试用一下Django,它是最简单的选择。SQLite包含在Python中,所以你不需要另外安装其他任何东西来支持你的数据库。然而,当你开始第一个真正的项目时,可能希望使用更可扩展的数据库(如PostgreSQL),以避免后面令人头疼的数据库切换。

如果你希望使用其他数据库,请安装相应的 数据库绑定 ,并更改 DATABASES 'default' 条目中以下的键以匹配你的数据库连接设置:

  • ENGINE'django.db.backends.sqlite3''django.db.backends.postgresql''django.db.backends.mysql' 或者 'django.db.backends.oracle' 。其它的后台 也是可以的

  • NAME – 数据库的名称。 如果你使用SQLite,数据库将是你计算机上的一个文件;在这种情况下, NAME 应该是该文件的完整绝对路径,包括文件名。 默认值是 os.path.join(BASE_DIR, 'db.sqlite3') ,它将文件保存在你项目的目录中。

如果你不使用SQLite作为数据库,则必须添加其他设置,如 USERPASSWORDHOST

更多的细节,请参见 DATABASES 的参考文档。

对于SQLite以外的数据库

如果使用SQLite之外的数据库,请确保你已经创建了一个数据库。

可以在你的数据库的交互式提示命令下,使用“ `CREATE

DATABASE database_name;` ”创建它。

还要确保 mysite/settings.py 中提供的数据库用户具有“创建数据库”权限。

这允许自动创建 test database ,这将在以后的教程中进行。

如果你使用SQLite,你不需要事先创建任何东西 —— 数据库文件将会在需要的时候自动创建。

当你编辑 mysite/settings.py 时,请设置 TIME_ZONE 为你自己的时区。

另外,请注意文件顶端的 INSTALLED_APPS 设置。它保存这个Django实例中激活的所有的Django应用的名字。应用可以在多个项目中使用,而且你可以将这些应用打包和分发给其他人在他们的项目中使用。

默认情况下, INSTALLED_APPS 包含下面的应用,它们都是Django 与生俱来的:

这些应用,默认包含在Django中,以方便通用场合下使用。

其中一些应用使用至少一个数据库表,因此我们需要在数据库中创建表,然后才能使用它们。 要做到这一点,请运行以下命令:

$ python manage.py migrate

migrate 查看 INSTALLED_APPS 设置并根据 mysite/settings.py 文件中的数据库设置创建任何必要的数据库表,数据库的迁移还会跟踪应用的变化(我们稍后会讲到)。

你会看到对每次迁移有一条信息。如果你感兴趣,可以运行你数据库的命令行工具来查看Django创建了哪些表: \dt (PostgreSQL), SHOWTABLES(MySQL),

.schema(SQLite), or SELECT TABLE_NAME FROM USER_TABLES
(Oracle)

创建模型

现在,我们将使用额外的元数据来定义你的模型 — 主要是数据库的布局。

在这个简单的投票应用中,我们将创建两个模型: QuestionChoiceQuestion对象具有一个question_text(问题)属性和一个publish_date(发布时间)属性。 Choice有两个字段:选择的内容和选择的得票统计。 每个Choice与一个Question关联。

这些概念通过简单的Python类来表示。 编辑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)

上述代码非常直观。每个模型都用一个类表示,该类继承自 django.db.models.Model 。每个模型都有一些类变量,在模型中每个类变量都代表了数据库中的一个字段。

每个字段通过 Field 类的一个实例表示 —— 例如字符字段 CharField 和日期字段 DateTimeField 。这种方法告诉Django,每个字段中保存着什么类型的数据。

每个 Field实例的名字(例如 pub_datequestion_text )就是字段的名字,并且是机器可读的格式。你将在Python代码中使用到它的值,并且你的数据库将把它用作表的列名。

你可以使用 Field的第一个可选的参数来指定一个人类可读的名字,这是可选的。这用于Django的几个内省部分,它可以作为文档。如果没有提供这个参数,Django将使用那个机器可读的名字(实例名)。在这个例子中,我们只为 Question.pub_date 定义一个人类可读的名字。对于这个模型中其他的字段,机器可读的名字(实例名)足以充分地表达出它的含义。

某些 Field 类具有必选的参数。例如, CharField 要求你给它一个 max_length
这个参数不仅用于数据库模式,而且数据验证中也会用到,我们稍后会看到。

一个 Field 可以有很多不同的可选参数; 比如在这里, 我们给 votes 设置默认值( default )为0。

最后,注意我们使用 ForeignKey 定义了一个关联。它告诉Django每个 Choice 都只关联一个 Question 。Django支持所有常见的数据库关系:多对一,多对多和一一对应。

激活模型

上面那段简短的模型代码给了Django很多信息。 有了这些代码,Django就能够自动完成以下两个功能:

  • 为该应用创建数据库表(CREATE TABLE 语句)。
  • 为Question对象和Choice对象创建一个访问数据库的python API。

但是,我们首先得告诉项目:polls应用已经安装。

要在我们的项目中包含该应用程序,我们需要在 INSTALLED_APPS 设置中添加对其配置类的引用。 PollsConfig 类位于 polls/apps.py 文件中,因此其点分路径为 'polls.apps.PollsConfig' 。编辑 mysite/settings.py 文件,并将点分路径添加到 INSTALLED_APPS 设置。

看起来像这样:

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,已经对模型做了一些更改(在这个例子中,你创建了一个新的模型)并且会将这些更改记录为迁移文件

迁移Django储存模型(即你的数据库模式)的变化的方式 — 它们只是磁盘上的文件。如果你喜欢,可以阅读你的新模型的迁移;它是文件 polls/migrations/0001_initial.py 。不用担心,Django不要求你在每次Django生成迁移文件之后都要阅读这些文件,但是它们被设计成可人为编辑的形式,以便你可以手工稍微修改一下Django的某些具体行为。

有一个命令可以运行这些迁移文件并自动管理你的数据库模式 —— 它叫做 migrate ,我们一会儿会用到它 —— 但是首先,让我们看一下迁移行为将会执行哪些SQL语句。 sqlmigrate 命令接收迁移文件的名字并返回它们的SQL语句:

$ python manage.py sqlmigrate polls 0001

应该能看到类似如下输出

BEGIN;
--
-- Create model Choice
--
CREATE TABLE "polls_choice" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "choice_text" varchar(200) NOT NULL, "votes" integer NOT NULL);
--
-- Create model Question
--
CREATE TABLE "polls_question" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "question_text" varchar(200) NOT NULL, "pub_date" datetime NOT NULL);
--
-- Add field question to choice
--
ALTER TABLE "polls_choice" RENAME TO "polls_choice__old";
CREATE TABLE "polls_choice" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "choice_text" varchar(200) NOT NULL, "votes" integer NOT NULL, "question_id" integer NOT NULL REFERENCES "polls_question" ("id"));
INSERT INTO "polls_choice" ("id", "choice_text", "votes", "question_id") SELECT "id", "choice_text", "votes", NULL FROM "polls_choice__old";
DROP TABLE "polls_choice__old";
CREATE INDEX "polls_choice_question_id_c5b4b260" ON "polls_choice" ("question_id");
COMMIT;

请注意以下几点:

  • 输出的具体内容会依据你使用的数据库而不同。 以上例子使用的数据库是PostgreSQL。

  • 表名是自动生成的,由app的名字( polls )和模型名字的小写字母组合而成 —— questionchoice 。 (你可以重写这个行为。)

  • 主键(id)是自动添加的。 (你也可以重写这个行为。)

  • 按照惯例,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 以在你的数据库中创建模型所对应的表:

$ python manage.py migrate

Operations to perform:
  Apply all migrations: admin, auth, contenttypes, polls, sessions
Running migrations:
  Applying polls.0001_initial... OK

migrate 命令会找出所有还没有被应用的迁移文件(Django使用数据库中一个叫做 django_migrations 的特殊表来追踪哪些迁移文件已经被应用过),并且在你的数据库上运行它们 —— 本质上来讲,就是使你的数据库模式和你改动后的模型进行同步。

迁移功能非常强大,可以让你在开发过程中不断修改你的模型而不用删除数据库或者表然后再重新生成一个新的 —— 它专注于升级你的数据库且不丢失数据。

我们将在本教程的后续章节对迁移进行深入地讲解,但是现在,请记住实现模型变更的三个步骤:

有单独的命令来制作和应用迁移的原因是因为您将提交迁移到您的版本控制系统并将其发送到您的应用程序;它们不仅可以使您的开发更容易,而且还可以被其他开发人员和生产中使用。

玩转API

现在,让我们进入Python的交互式shell,玩转这些Django提供给你的API。 使用如下命令来调用Python shell:

$ python manage.py shell

一旦你进入shell,请浏览 database API

In [1]: from polls.models import Question, Choice # Import the model classes we just wrote. 

# No questions are in the system yet.# 系统中还没有question。
In [2]: Question.objects.all()
Out[2]: <QuerySet []>

# 创建一个新的Question。
# 默认的settings文件中启用对时区的支持,所以
# Django期待pub_date为一个带有tzinfo的datetime。 使用timezone.now()
# 而不是datetime.datetime.now(),它将完成正确的事情。
In [3]: # Create a new Question.^M
   ...: # Support for time zones is enabled in the default settings file, so^M
   ...: # Django expects a datetime with tzinfo for pub_date. Use timezone.now()^M
   ...: # instead of datetime.datetime.now() and it will do the right thing.
   ...: from django.utils import timezone

In [4]: q = Question(question_text="What's new?", pub_date=timezone.now())


# 保存这个对象到数据库中。 你必须显示地调用save()。
# Save the object into the database. You have to call save() explicitly.
In [5]: q.save()

# 现在它有一个ID。 注意,它可能为"1L"而不是"1",取决于
# 你使用的数据库。 这不是什么事;它只是表示你的
# 数据库后端返回的是Python长整形
# 对象。
# 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.
In [6]: q.id
Out[6]: 1

# 通过Python属性访问模型字段的值。
# Access model field values via Python attributes.
>>> q.question_text
In [7]: q.question_text
Out[7]: "What's new?"

In [8]: q.pub_date
Out[8]: datetime.datetime(2017, 11, 30, 13, 30, 20, 705812, tzinfo=<UTC>)

# 通过改变属性来改变字段的值,然后调用save()。
# Change values by changing the attributes, then calling save().
In [9]: q.question_text = "What's up?"
In [10]: q.save()

# objects.all()显示数据库中所有的question。
# objects.all() displays all the questions in the database.
In [11]: Question.objects.all()
Out[11]: <QuerySet [<Question: Question object>]>

先等一下.。 <Questionobject> 完全是这个对象无意义的表示。让我们来修复这个问题,编辑 Question 模型(在 polls/models.py 文件中)并添加一个 __str__() 方法给 QuestionChoice

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)

给你的模型添加 __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)

保存这些改动,然后通过python manage.py shell再次打开一个新的Python 交互式shell:

 from polls.models import Question, Choice

# 确认我们的 __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?>]>

# 获取今年发布的问题
>>> 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 创建几个 Choices. 创建一个新的
# Choice 对象, 使用 INSERT 语句将选项添加到可用选项的集合并返回新的“Choice”对象。
# (合并至上句) Django 通过创建
# 一个集合来控制“在另一端的”可以通过 API 访问的外键。
# (例如,一个“问题”的“选项”)
>>> q = Question.objects.get(pk=1)

# 显示任何有关的选项 ——目前还没有.
>>> q.choice_set.all()
<QuerySet []>

# 创建三个choices。
>>> 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

# AIP 根据需要自动创建关系。
# 可以使用双下划线分隔关系。
# 它的工作机制是尽可能深的创建关系,而没有限制。
# 通过 pub_date 查找今年内创建的问题的所有选项
# (再次使用了之前创建的 'current_year' 变量).
>>> Choice.objects.filter(question__pub_date__year=current_year)
<QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]>

# 让我删除一个选项. 使用 delete() 方法.
>>> c = q.choice_set.filter(choice_text__startswith='Just hacking')
>>> c.delete()

Django后台管理简介

创建管理员用户

$ python manage.py createsuperuser

Username (leave blank to use 'xc'): admin
Email address: 986284130@qq.com
Password:
Password (again):
This password is too short. It must contain at least 8 characters.
This password is too common.
This password is entirely numeric.
Password:
Password (again):
Superuser created successfully.

启动开发服务器

Django的管理后台站点是默认启用的。 让我们启动开发服务器,然后探索它。

如果服务器没有运行,像下面这样启动它:

$ python manage.py runserver

现在,打开一个浏览器访问你本地域名中的 “/admin/” — 例如http://127.0.0.1:8000/admin/。你应该会看到管理后台站点的登录界面:

image

进入管理员后台站点

用你在前面创建的超级用户账号来登录这个站点。 你应该会看到Django管理后台站点的首页面:


image

让投票应用在管理后台站点中可编辑

但我们的投票应用在哪儿? 它没有显示在管理后台站点的首页面上。

只需要做一件事:我们需要告诉管理站点Question 对象要有一个管理界面。 要做这件事,需要打开polls/admin.py文件,把它编辑成这样:

from django.contrib import admin

from .models import Question

admin.site.register(Question)

探索管理后台的功能

现在,我们已经在管理站点中注册了Question对象,Django应该把它显示在管理站点的首页面上:

image

点击“Questions”。 现在,你会进入Question的“变更列表”。 这个界面显示了数据库中的所有question,你可以选择一个来更改它。 我们之前创建的“What's up?”问题


image

单击“What’s up?”来编辑它:


image

注意事项:

  • 这个表单是根据 Question 模型文件自动生成的。

  • 模型中不同类型的字段( DateTimeFieldCharField )会对应相应的HTML输入控件。 每一种类型的字段,Django管理站点都知道如何显示它们。

  • 每个 DateTimeField 字段都会有个方便的JavaScript快捷方式。 Date有个“Today”的快捷方式和一个弹出式日历,time栏有个“Now”的快捷方式和一个列出常用时间选项的弹出式窗口。

界面的底部有几个按钮:

  • Save — 保存更改,并返回当前类型对象的变更列表页面。
  • Save and continue editing — 保存更改并且重新载入当前对象的管理界面。
  • Save and add another — 保存更改并且载入一个当前类型对象的新的、空白的表单。
  • Delete — 显示一个删除确认界面。

如果“Date published”的值与你在 Tutorial 1 中创建问题的时间不符,可能意味着你忘记为 TIME_ZONE 设置正确的值设置。修改它,然后重新载入这个界面,检查一下正确的值是否出现。

通过“Today”和“Now”这两个快捷方式来更改“Date published”字段。然后点击“保存并继续编辑”,然后点击右上角的“历史记录”。

你将看到一个页面,列出了通过Django管理界面对此对象所做的全部更改的清单,包含有时间戳和修改人的姓名等信息:


image
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,165评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,720评论 1 298
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,849评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,245评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,596评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,747评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,977评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,708评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,448评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,657评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,141评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,493评论 3 258
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,153评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,108评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,890评论 0 198
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,799评论 2 277
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,685评论 2 272

推荐阅读更多精彩内容