设置数据库

DjangoDjangoBeginner
立即练习

💡 本教程由 AI 辅助翻译自英文原版。如需查看原文,您可以 切换至英文原版

简介

本教程从《创建一个基本投票应用》结束的地方开始。我们将设置数据库,创建你的第一个模型,并快速介绍Django自动生成的管理站点。


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL django(("`Django`")) -.-> django/DatabaseModelsandMigrationsGroup(["`Database, Models, and Migrations`"]) django(("`Django`")) -.-> django/DevelopmentandAdministrationToolsGroup(["`Development and Administration Tools`"]) django/DatabaseModelsandMigrationsGroup -.-> django/models("`Models`") django/DatabaseModelsandMigrationsGroup -.-> django/databases("`Databases`") django/DatabaseModelsandMigrationsGroup -.-> django/schemaeditor("`SchemaEditor`") django/DevelopmentandAdministrationToolsGroup -.-> django/django_admin("`Django Admin`") django/DevelopmentandAdministrationToolsGroup -.-> django/contrib_packages("`Contrib Packages`") subgraph Lab Skills django/models -.-> lab-153742{{"`设置数据库`"}} django/databases -.-> lab-153742{{"`设置数据库`"}} django/schemaeditor -.-> lab-153742{{"`设置数据库`"}} django/django_admin -.-> lab-153742{{"`设置数据库`"}} django/contrib_packages -.-> lab-153742{{"`设置数据库`"}} end

数据库设置

现在,打开 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作为数据库,则必须添加其他设置,如 USERPASSWORDHOST。有关更多详细信息,请参阅 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_textpub_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 语句)。
  • 创建一个用于访问 QuestionChoice 对象的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;

请注意以下几点:

  • 确切的输出将根据你使用的数据库而有所不同。上面的示例是为PostgreSQL生成的。
  • 表名是通过组合应用的名称(polls)和模型的小写名称自动生成的 —— questionchoice。(你可以覆盖此行为。)
  • 主键(ID)会自动添加。(你也可以覆盖此设置。)
  • 按照惯例,Django会在外部键字段名称后附加 "_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 实用工具功能的完整信息。

玩转API

现在,让我们进入交互式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 文件中)并为 QuestionChoice 添加一个 ~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 datetimefrom 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是在新闻编辑室环境中编写的,“内容发布者”和“公共”站点之间有非常明确的分隔。站点管理员使用该系统添加新闻报道、活动、体育比分等,这些内容会显示在公共站点上。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/。你应该会看到管理界面的登录屏幕:

Django管理界面登录屏幕

由于默认情况下启用了 翻译 </topics/i18n/translation>,如果你设置了 LANGUAGE_CODE,登录屏幕将以指定的语言显示(前提是Django有相应的翻译)。

进入管理站点

现在,尝试使用你在上一步中创建的超级用户账户登录。你应该会看到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知道它应该显示在管理界面的首页上:

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中练习更多实验来提升你的技能。

您可能感兴趣的其他 Django 教程