简介
本教程从《创建一个基本投票应用》结束的地方开始。我们将设置数据库,创建你的第一个模型,并快速介绍Django自动生成的管理站点。
本教程从《创建一个基本投票应用》结束的地方开始。我们将设置数据库,创建你的第一个模型,并快速介绍Django自动生成的管理站点。
现在,打开 mysite/settings.py
。它是一个普通的Python模块,其中的模块级变量代表Django设置。
默认情况下,配置使用SQLite。如果你是数据库新手,或者只是想尝试一下Django,这是最简单的选择。SQLite包含在Python中,所以你不需要安装任何其他东西来支持你的数据库。然而,当你开始第一个实际项目时,你可能想要使用像PostgreSQL这样更具可扩展性的数据库,以避免日后切换数据库带来的麻烦。
如果你希望使用其他数据库,请安装相应的 数据库绑定 <database-installation>
,并在 DATABASES
的 'default'
项中更改以下键,以匹配你的数据库连接设置:
ENGINE <DATABASE-ENGINE>
-- 可以是 'django.db.backends.sqlite3'
、'django.db.backends.postgresql'
、'django.db.backends.mysql'
或 'django.db.backends.oracle'
。其他后端也 可用<third-party-notes>
。NAME
-- 你的数据库名称。如果你使用SQLite,数据库将是你计算机上的一个文件;在这种情况下,NAME
应该是该文件的完整绝对路径,包括文件名。默认值 BASE_DIR / 'db.sqlite3'
将把文件存储在你的项目目录中。如果你不使用SQLite作为数据库,则必须添加其他设置,如 USER
、PASSWORD
和 HOST
。有关更多详细信息,请参阅 DATABASES
的参考文档。
对于非SQLite数据库
如果你使用的是除SQLite之外的数据库,请确保此时已经创建了数据库。在数据库的交互式提示符中使用 “CREATE DATABASE database_name;
” 来创建。
还要确保 mysite/settings.py
中提供的数据库用户具有 “创建数据库” 权限。这允许自动创建一个 测试数据库 <the-test-database>
,这在后面的教程中会用到。
如果你使用SQLite,则无需事先创建任何东西 —— 数据库文件将在需要时自动创建。
在编辑 mysite/settings.py
时,将 TIME_ZONE
设置为你的时区。
另外,请注意文件顶部的 INSTALLED_APPS
设置。它包含在此Django实例中激活的所有Django应用的名称。应用可以在多个项目中使用,你可以打包并分发它们供其他人在他们的项目中使用。
默认情况下,INSTALLED_APPS
包含以下所有随Django一起提供的应用:
django.contrib.admin
-- 管理站点。你很快就会用到它。django.contrib.auth
-- 一个认证系统。django.contrib.contenttypes
-- 一个内容类型框架。django.contrib.sessions
-- 一个会话框架。django.contrib.messages
-- 一个消息框架。django.contrib.staticfiles
-- 一个管理静态文件的框架。默认情况下包含这些应用是为了方便常见情况。
不过,其中一些应用至少使用一个数据库表,所以我们需要在数据库中创建表才能使用它们。为此,请运行以下命令:
cd ~/project/mysite
python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, contenttypes, sessions
Running migrations:
Applying contenttypes.0001_initial... OK
Applying auth.0001_initial... OK
Applying admin.0001_initial... OK
Applying admin.0002_logentry_remove_auto_add... OK
Applying admin.0003_logentry_add_action_flag_choices... OK
Applying contenttypes.0002_remove_content_type_name... OK
Applying auth.0002_alter_permission_name_max_length... OK
Applying auth.0003_alter_user_email_max_length... OK
Applying auth.0004_alter_user_username_opts... OK
Applying auth.0005_alter_user_last_login_null... OK
Applying auth.0006_require_contenttypes_0002... OK
Applying auth.0007_alter_validators_add_error_messages... OK
Applying auth.0008_alter_user_username_max_length... OK
Applying auth.0009_alter_user_last_name_max_length... OK
Applying auth.0010_alter_group_name_max_length... OK
Applying auth.0011_update_proxy_permissions... OK
Applying auth.0012_alter_user_first_name_max_length... OK
Applying sessions.0001_initial... OK
migrate
命令会查看 INSTALLED_APPS
设置,并根据 mysite/settings.py
文件中的数据库设置以及应用附带的数据库迁移(我们稍后会介绍)创建任何必要的数据库表。你会看到它应用的每个迁移的消息。如果你感兴趣,可以运行数据库的命令行客户端并输入 \dt
(PostgreSQL)、SHOW TABLES;
(MariaDB、MySQL)、.tables
(SQLite)或 SELECT TABLE_NAME FROM USER_TABLES;
(Oracle)来显示Django创建的表。
对于极简主义者
如我们上面所说,默认应用是为常见情况而包含的,但不是每个人都需要它们。如果你不需要其中任何一个或全部,可以在运行 migrate
之前,随意从 INSTALLED_APPS
中注释掉或删除相应的行。migrate
命令只会运行 INSTALLED_APPS
中应用的迁移。
现在我们将定义你的模型 —— 本质上就是你的数据库布局,并附带额外的元数据。
模型是关于你的数据的唯一权威信息源。它包含了你正在存储的数据的基本字段和行为。Django遵循 DRY原则 <dry>
。目标是在一个地方定义你的数据模型,并从中自动派生其他内容。
这包括迁移 —— 例如,与Ruby On Rails不同,迁移完全从你的模型文件派生而来,本质上是Django可以遍历的历史记录,用于更新你的数据库模式以匹配当前模型。
在我们的投票应用中,我们将创建两个模型:Question
(问题)和 Choice
(选项)。一个 Question
有一个问题和一个发布日期。一个 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
的类表示。每个模型都有许多类变量,每个类变量都表示模型中的一个数据库字段。
每个字段由一个 ~django.db.models.Field
类的实例表示 —— 例如,字符字段用 ~django.db.models.CharField
表示,日期时间字段用 ~django.db.models.DateTimeField
表示。这告诉Django每个字段存储的数据类型。
每个 ~django.db.models.Field
实例的名称(例如 question_text
或 pub_date
)是字段的名称,采用机器友好的格式。你将在Python代码中使用这个值,并且你的数据库将使用它作为列名。
你可以使用 ~django.db.models.Field
的第一个可选位置参数来指定一个人类可读的名称。这在Django的一些自省部分中使用,并且它还可以作为文档。如果没有提供这个字段,Django将使用机器可读的名称。在这个例子中,我们只为 Question.pub_date
定义了一个人类可读的名称。对于这个模型中的所有其他字段,字段的机器可读名称就足以作为它的人类可读名称。
一些 ~django.db.models.Field
类有必需的参数。例如,~django.db.models.CharField
要求你给它一个 ~django.db.models.CharField.max_length
。这不仅用于数据库模式,还用于验证,我们很快就会看到。
一个 ~django.db.models.Field
也可以有各种可选参数;在这种情况下,我们将 votes
的 ~django.db.models.Field.default
值设置为0。
最后,请注意使用 ~django.db.models.ForeignKey
定义了一种关系。这告诉Django每个 Choice
都与一个 Question
相关。Django支持所有常见的数据库关系:多对一、多对多和一对一。
那一小段模型代码为Django提供了很多信息。有了它,Django能够:
CREATE TABLE
语句)。Question
和 Choice
对象的Python数据库访问API。但首先我们需要告诉我们的项目 polls
应用已安装。
Django应用是 “可插拔的”:你可以在多个项目中使用一个应用,并且你可以分发应用,因为它们不必与特定的Django安装绑定。
要将应用包含在我们的项目中,我们需要在 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 Question
- Create model Choice
通过运行 makemigrations
,你是在告诉Django你已经对你的模型做了一些更改(在这种情况下,你创建了新模型),并且你希望这些更改被存储为一个 迁移。
迁移是Django存储对模型(以及因此对你的数据库模式)的更改的方式 —— 它们是磁盘上的文件。如果你愿意,你可以阅读新模型的迁移文件;它是 polls/migrations/0001_initial.py
文件。别担心,不期望你每次Django创建迁移时都去阅读它们,但它们设计为可人工编辑,以防你想手动调整Django更改事物的方式。
有一个命令可以为你运行迁移并自动管理你的数据库模式 —— 那就是 migrate
,我们稍后会讲到 —— 但首先,让我们看看那个迁移会运行什么SQL。sqlmigrate
命令接受迁移名称并返回它们的SQL:
python manage.py sqlmigrate polls 0001
你应该会看到类似于以下内容(我们为了可读性重新格式化了它):
BEGIN;
--
-- Create model Question
--
CREATE TABLE "polls_question" (
"id" bigint NOT NULL PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY,
"question_text" varchar(200) NOT NULL,
"pub_date" timestamp with time zone NOT NULL
);
--
-- Create model Choice
--
CREATE TABLE "polls_choice" (
"id" bigint NOT NULL PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY,
"choice_text" varchar(200) NOT NULL,
"votes" integer NOT NULL,
"question_id" bigint NOT NULL
);
ALTER TABLE "polls_choice"
ADD CONSTRAINT "polls_choice_question_id_c5b4b260_fk_polls_question_id"
FOREIGN KEY ("question_id")
REFERENCES "polls_question" ("id")
DEFERRABLE INITIALLY DEFERRED;
CREATE INDEX "polls_choice_question_id_c5b4b260" ON "polls_choice" ("question_id");
COMMIT;
请注意以下几点:
polls
)和模型的小写名称自动生成的 —— question
和 choice
。(你可以覆盖此行为。)"_id"
。(是的,你也可以覆盖此设置。)FOREIGN KEY
约束明确表示。不用担心 DEFERRABLE
部分;它是在告诉PostgreSQL直到事务结束才强制执行外键。auto_increment
(MySQL)、bigint PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY
(PostgreSQL)或 integer primary key autoincrement
(SQLite)会为你自动处理。字段名称的引用也是如此 —— 例如,使用双引号或单引号。sqlmigrate
命令实际上不会在你的数据库上运行迁移 —— 相反,它会将其打印到屏幕上,以便你可以看到Django认为需要什么SQL。这对于检查Django要做什么或者如果你有需要SQL脚本进行更改的数据库管理员来说很有用。如果你感兴趣,你也可以运行 python manage.py check <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
的特殊表来跟踪哪些迁移已应用),并针对你的数据库运行它们 —— 本质上,是将你对模型所做的更改与数据库中的模式同步。
迁移非常强大,它允许你在开发项目时随着时间的推移更改模型,而无需删除你的数据库或表并重新创建它们 —— 它专门用于实时升级你的数据库,而不会丢失数据。我们将在本教程的后面部分更深入地介绍它们,但现在,请记住进行模型更改的三步指南:
models.py
中)。python manage.py makemigrations <makemigrations>
为这些更改创建迁移。python manage.py migrate <migrate>
将这些更改应用到数据库。之所以有单独的命令来创建和应用迁移,是因为你将把迁移提交到你的版本控制系统并与你的应用一起发布;它们不仅使你的开发更容易,其他开发人员也可以使用它们,并且可以用于生产环境。
阅读 django-admin文档 </ref/django-admin>
以获取有关 manage.py
实用工具功能的完整信息。
现在,让我们进入交互式Python shell,来体验一下Django为你提供的免费API。要调用Python shell,使用以下命令:
python manage.py shell
我们使用这个命令而不是简单地输入 “python”,是因为 manage.py
设置了 DJANGO_SETTINGS_MODULE
环境变量,它为Django提供了到你的 mysite/settings.py
文件的Python导入路径。
进入shell后,探索一下 数据库API </topics/db/queries>
:
>>> from polls.models import Choice, Question ## 导入我们刚刚编写的模型类。
## 系统中还没有问题。
>>> Question.objects.all()
<QuerySet []>
## 创建一个新问题。
## 默认设置文件中启用了时区支持,所以
## Django期望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属性访问模型字段值。
>>> q.question_text
"What's new?"
>>> q.pub_date
datetime.datetime(2023, 9, 7, 1, 18, 48, 335644, tzinfo=datetime.timezone.utc)
## 通过更改属性然后调用save()来更改值。
>>> q.question_text = "What's up?"
>>> q.save()
## objects.all() 显示数据库中的所有问题。
>>> Question.objects.all()
<QuerySet [<Question: Question object (1)>]>
等一下。<Question: Question object (1)>
对这个对象的表示没什么用。让我们通过编辑 Question
模型(在 polls/models.py
文件中)并为 Question
和 Choice
添加一个 ~django.db.models.Model.__str__
方法来解决这个问题:
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
为你的模型添加 ~django.db.models.Model.__str__
方法很重要,这不仅对你在处理交互式提示符时方便,而且因为对象的表示在Django自动生成的管理界面中到处都有用到。
让我们也为这个模型添加一个自定义方法:
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中的时区处理,可以在 时区支持文档 </topics/i18n/timezones>
中了解更多。
保存这些更改,然后通过 再次运行 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?>]>
## 获取今年发布的问题。
>>> 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
## 给这个问题添加几个选项。create调用构造一个新的
## Choice对象,执行INSERT语句,将选项添加到可用选项集中
## 并返回新的Choice对象。Django创建一个集合来保存
## 外键关系的 “另一边”
## (例如,一个问题的选项),可以通过API访问。
>>> q = Question.objects.get(pk=1)
## 显示相关对象集中的任何选项 —— 目前还没有。
>>> q.choice_set.all()
<QuerySet []>
## 创建三个选项。
>>> 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
## API会根据你的需要自动跟踪关系。
## 使用双下划线分隔关系。
## 这可以深入任意多层;没有限制。
## 找到今年发布的任何问题的所有选项
## (重用我们上面创建的 '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()
有关模型关系的更多信息,请参阅 访问相关对象 </ref/models/relations>
。有关如何使用双下划线通过API执行字段查找的更多信息,请参阅 字段查找 <field-lookups-intro>
。有关数据库API的完整详细信息,请参阅我们的 数据库API参考 </topics/db/queries>
。
为员工或客户生成用于添加、更改和删除内容的管理站点是一项繁琐的工作,不需要太多创造力。因此,Django完全自动化了模型管理界面的创建。
Django是在新闻编辑室环境中编写的,“内容发布者”和“公共”站点之间有非常明确的分隔。站点管理员使用该系统添加新闻报道、活动、体育比分等,这些内容会显示在公共站点上。Django解决了为站点管理员创建统一界面来编辑内容的问题。
管理界面不是供站点访问者使用的。它是为站点管理员准备的。
首先,我们需要创建一个可以登录到管理站点的用户。运行以下命令:
python manage.py createsuperuser
输入你想要的用户名,然后按回车键。
用户名:admin
然后系统会提示你输入想要的电子邮件地址:
电子邮件地址:[email protected]
最后一步是输入你的密码。系统会要求你输入两次密码,第二次是对第一次的确认。
密码:12345678
再次输入密码:12345678
此密码太常见。
此密码全是数字。
无论如何都要绕过密码验证并创建用户吗?[y/N]:y
超级用户创建成功。
Django管理站点默认是激活的。让我们启动开发服务器并进行探索。
如果服务器没有运行,像这样启动它:
python manage.py runserver
现在,在VNC标签中打开一个网页浏览器,然后在你的本地域名上访问“/admin/” —— 例如,http://127.0.0.1:8000/admin/
。你应该会看到管理界面的登录屏幕:
由于默认情况下启用了 翻译 </topics/i18n/translation>
,如果你设置了 LANGUAGE_CODE
,登录屏幕将以指定的语言显示(前提是Django有相应的翻译)。
现在,尝试使用你在上一步中创建的超级用户账户登录。你应该会看到Django管理界面的首页:
你应该会看到几种可编辑的内容类型:组和用户。它们由Django附带的认证框架 django.contrib.auth
提供。
但是我们的投票应用在哪里呢?它没有显示在管理界面的首页上。
只需要再做一件事:我们需要告诉管理界面 Question
对象有一个管理界面。要做到这一点,打开 polls/admin.py
文件,并将其编辑成如下所示:
from django.contrib import admin
from.models import Question
admin.site.register(Question)
现在我们已经注册了 Question
,Django知道它应该显示在管理界面的首页上:
点击“问题”。现在你在问题的“更改列表”页面。这个页面显示了数据库中的所有问题,并让你选择一个进行更改。这里有我们之前创建的“What's up?”问题:
点击“What's up?”问题进行编辑:
这里需要注意的几点:
Question
模型自动生成的。~django.db.models.DateTimeField
,~django.db.models.CharField
)对应相应的HTML输入小部件。每种字段类型都知道如何在Django管理界面中显示自己。~django.db.models.DateTimeField
都有免费的JavaScript快捷方式。日期有一个“今天”快捷方式和日历弹出窗口,时间有一个“现在”快捷方式和一个列出常用输入时间的方便弹出窗口。页面底部为你提供了几个选项:
如果“发布日期”的值与你在创建基本投票应用中创建问题时的时间不匹配,可能意味着你忘记为 TIME_ZONE
设置正确的值。更改它,重新加载页面并检查是否显示了正确的值。
通过点击“今天”和“现在”快捷方式更改“发布日期”。然后点击“保存并继续编辑”。然后点击右上角的“历史记录”。你会看到一个页面,列出了通过Django管理界面对此对象所做的所有更改,以及进行更改的人的时间戳和用户名:
当你熟悉了模型API并熟悉了管理站点后,请阅读创建公共接口视图,以了解如何为我们的投票应用添加更多视图。
恭喜你!你已经完成了“设置数据库”实验。你可以在LabEx中练习更多实验来提升你的技能。