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

来自菜鸟教程
Django/docs/2.2.x/intro/tutorial02
跳转至:导航、​搜索

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

本教程从 教程 1 停止的地方开始。 我们将设置数据库,创建您的第一个模型,并快速介绍 Django 自动生成的管理站点。

数据库配置

现在,打开mysite/settings.py。 它是一个普通的 Python 模块,具有代表 Django 设置的模块级变量。

默认情况下,配置使用 SQLite。 如果您不熟悉数据库,或者您只是对尝试 Django 感兴趣,这是最简单的选择。 SQLite 包含在 Python 中,因此您无需安装任何其他东西来支持您的数据库。 但是,在开始您的第一个实际项目时,您可能希望使用更具可扩展性的数据库(如 PostgreSQL),以避免以后出现数据库切换问题。

如果您希望使用其他数据库,请安装适当的 数据库绑定 并更改 :setting:`DATABASES` 'default' 项中的以下键以匹配您的数据库连接设置:

  • :设置:`引擎 ` - 任何一个'django.db.backends.sqlite3' ,'django.db.backends.postgresql' ,'django.db.backends.mysql' , 或者'django.db.backends.oracle' . 其他后端 也可用
  • :setting:`NAME` – 数据库的名称。 如果您使用 SQLite,数据库将是您计算机上的一个文件; 在这种情况下, :setting:`NAME` 应该是该文件的完整绝对路径,包括文件名。 默认值 os.path.join(BASE_DIR, 'db.sqlite3') 会将文件存储在您的项目目录中。

如果你没有使用 SQLite 作为你的数据库,额外的设置如 :setting:`USER`:setting:`PASSWORD`:setting:`HOST` 必须添加。 有关更多详细信息,请参阅 :setting:`DATABASES` 的参考文档。

SQLite 以外的其它数据库

如果您正在使用除 SQLite 之外的数据库,请确保此时您已经创建了一个数据库。 在数据库的交互式提示中使用“CREATE DATABASE database_name;”来执行此操作。

还要确保 mysite/settings.py 中提供的数据库用户具有“创建数据库”权限。 这允许自动创建 测试数据库 ,这将在后面的教程中用到。

如果您使用 SQLite,则无需事先创建任何内容 - 数据库文件将在需要时自动创建。


在编辑 mysite/settings.py 时,将 :setting:`TIME_ZONE` 设置为您的时区。

另外,请注意文件顶部的 :setting:`INSTALLED_APPS` 设置。 它包含在此 Django 实例中激活的所有 Django 应用程序的名称。 应用程序可以在多个项目中使用,您可以打包和分发它们以供其他人在他们的项目中使用。

默认情况下,:setting:`INSTALLED_APPS` 包含以下应用程序,所有这些应用程序都与 Django 一起提供:

这些应用被默认启用是为了给常规项目提供方便。

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

:djadmin:`migrate` 命令查看 :setting:`INSTALLED_APPS` 设置并根据 mysite/settings.py 文件中的数据库设置创建任何必要的数据库表以及应用程序附带的数据库迁移(我们稍后会介绍)。 对于它应用的每个迁移,您都会看到一条消息。 如果您有兴趣,请为您的数据库运行命令行客户端并键入 \dt (PostgreSQL)、SHOW TABLES; (MySQL)、.schema (SQLite) 或 SELECT TABLE_NAME FROM USER_TABLES; (Oracle) 显示 Django 创建的表。

写给极简主义者

就像我们上面所说的,默认应用程序包含在常见情况下,但并不是每个人都需要它们。 如果您不需要任何或全部,请在运行 :djadmin:`migrate` 之前随意注释或删除 :setting:`INSTALLED_APPS` 中的适当行。 :djadmin:`migrate` 命令只会为 :setting:`INSTALLED_APPS` 中的应用程序运行迁移。


创建模型

现在我们将定义您的模型——本质上是您的数据库布局,以及额外的元数据。

设计哲学

模型是有关您的数据的唯一、确定的真实来源。 它包含您存储的数据的基本字段和行为。 Django 遵循 DRY 原则 。 目标是在一个地方定义您的数据模型并自动从中派生东西。

来介绍一下迁移 - 举个例子,不像 Ruby On Rails,Django 的迁移代码是由你的模型文件自动生成的,它本质上是个历史记录,Django 可以用它来进行数据库的滚动更新,通过这种方式使其能够和当前的模型匹配。


在我们简单的投票应用程序中,我们将创建两个模型:QuestionChoiceQuestion 有一个问题和一个发布日期。 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)

代码很简单。 每个模型都由一个类表示,该类是 django.db.models.Model 的子类。 每个模型都有许多类变量,每个类变量代表模型中的一个数据库字段。

每个字段都由一个 Field 类的实例表示——例如,CharField 代表字符字段,DateTimeField 代表日期时间。 这告诉 Django 每个字段保存的数据类型。

每个 Field 实例的名称(例如 question_textpub_date) 是机器友好格式的字段名称。 您将在 Python 代码中使用此值,您的数据库将使用它作为列名。

您可以使用 Field 的可选第一个位置参数来指定一个人类可读的名称。 这在 Django 的几个内省部分中使用,并且它兼作文档。 如果未提供此字段,Django 将使用机器可读的名称。 在这个例子中,我们只为 Question.pub_date 定义了一个人类可读的名称。 对于此模型中的所有其他字段,该字段的机器可读名称将足以作为其人类可读的名称。

某些 Field 类具有必需的参数。 例如,CharField 要求你给它一个 max_length。 这不仅用于数据库模式,还用于验证,我们很快就会看到。

Field 也可以有各种可选参数; 在这种情况下,我们将 votesdefault 值设置为 0。

最后,请注意使用 ForeignKey 定义了一个关系。 这告诉 Django 每个 Choice 与单个 Question 相关。 Django 支持所有常见的数据库关系:多对一、多对多和一对一。


激活模型

一小段模型代码为 Django 提供了很多信息。 有了它,Django 能够:

  • 为此应用程序创建数据库架构(CREATE TABLE 语句)。
  • 创建用于访问 QuestionChoice 对象的 Python 数据库访问 API。

但首先我们需要告诉我们的项目 polls 应用程序已安装。

设计哲学

Django 应用程序是“可插入的”:您可以在多个项目中使用一个应用程序,也可以分发应用程序,因为它们不必绑定到给定的 Django 安装。


要将应用程序包含在我们的项目中,我们需要在 :setting:`INSTALLED_APPS` 设置中添加对其配置类的引用。 PollsConfig 类在 polls/apps.py 文件中,所以它的虚线路径是 'polls.apps.PollsConfig'。 编辑 mysite/settings.py 文件并将该虚线路径添加到 :setting:`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 应用程序。 让我们运行另一个命令:

你将会看到类似于下面这样的输出:

Migrations for 'polls':
  polls/migrations/0001_initial.py:
    - Create model Choice
    - Create model Question
    - Add field question to choice

通过运行 makemigrations,您告诉 Django 您已经对模型进行了一些更改(在本例中,您已经创建了新模型)并且您希望将这些更改存储为 [ X176X]迁移。

迁移是 Django 存储对模型(以及数据库架构)更改的方式——它们只是磁盘上的文件。 如果您愿意,可以阅读新模型的迁移; 它是文件 polls/migrations/0001_initial.py。 不用担心,您不会在每次 Django 制作时都阅读它们,但是它们被设计为可人工编辑的,以防您想手动调整 Django 改变事物的方式。

有一个命令可以为您运行迁移并自动管理您的数据库模式 - 这称为 :djadmin:`migrate`,我们稍后会介绍它 - 但首先,让我们看看什么 SQL该迁移将运行。 :djadmin:`sqlmigrate` 命令获取迁移名称并返回它们的 SQL:

您应该会看到类似于以下内容的内容(为了便于阅读,我们已经对其进行了重新格式化):

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 生成的。
  • 表格名称是通过组合应用程序名称 (polls) 和模型的小写名称 - questionchoice 自动生成的。 (您可以覆盖此行为。)
  • 主键 (ID) 是自动添加的。 (你也可以覆盖它。)
  • 按照惯例,Django 将 "_id" 附加到外键字段名称。 (是的,您也可以覆盖它。)
  • 外键关系由 FOREIGN KEY 约束明确。 不用担心 DEFERRABLE 零件; 这只是告诉 PostgreSQL 在事务结束之前不要强制执行外键。
  • 它针对您使用的数据库量身定制,因此可以处理特定于数据库的字段类型,例如 auto_increment (MySQL)、serial (PostgreSQL) 或 integer primary key autoincrement (SQLite)自动为你。 字段名称的引用也是如此——例如,使用双引号或单引号。
  • :djadmin:`sqlmigrate` 命令实际上并没有在你的数据库上运行迁移——它只是将它打印到屏幕上,这样你就可以看到 SQL Django 认为需要什么。 这对于检查 Django 将要做什么或是否有需要 SQL 脚本进行更改的数据库管理员很有用。

如果你有兴趣,你也可以跑 :djadmin:`python manage.py 检查 ` ; 这会检查项目中的任何问题,而无需进行迁移或接触数据库。

现在,再次运行 :djadmin:`migrate` 以在您的数据库中创建这些模型表:

:djadmin:`migrate` 命令获取所有尚未应用的迁移(Django 使用数据库中名为 django_migrations 的特殊表跟踪应用了哪些迁移)并针对它们运行它们您的数据库 - 本质上,将您对模型所做的更改与数据库中的架构同步。

迁移非常强大,可让您在开发项目时随时间更改模型,而无需删除数据库或表并创建新表 - 它专门用于实时升级数据库,而不会丢失数据。 我们将在本教程的后面部分更深入地介绍它们,但现在,请记住进行模型更改的三步指南:

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

阅读 django-admin 文档 以获取有关 manage.py 实用程序功能的完整信息。


初试 API

现在,让我们进入交互式 Python shell 并使用 Django 为您提供的免费 API。 要调用 Python shell,请使用以下命令:

我们使用它而不是简单地输入“python”,因为 manage.py 设置了 DJANGO_SETTINGS_MODULE 环境变量,它为 Django 提供了 mysite/settings.py 文件的 Python 导入路径。

进入 shell 后,探索 数据库 API

>>> from polls.models import Choice, Question  # Import the model classes we just wrote.

# No questions are in the system yet.
>>> Question.objects.all()
<QuerySet []>

# Create a new Question.
# Support for time zones is enabled in the default settings file, so
# Django expects a datetime with tzinfo for pub_date. Use timezone.now()
# instead of datetime.datetime.now() and it will do the right thing.
>>> from django.utils import timezone
>>> q = Question(question_text="What's new?", pub_date=timezone.now())

# Save the object into the database. You have to call save() explicitly.
>>> q.save()

# Now it has an ID.
>>> q.id
1

# Access model field values via Python attributes.
>>> q.question_text
"What's new?"
>>> q.pub_date
datetime.datetime(2012, 2, 26, 13, 0, 0, 775217, tzinfo=<UTC>)

# Change values by changing the attributes, then calling save().
>>> q.question_text = "What's up?"
>>> q.save()

# objects.all() displays all the questions in the database.
>>> Question.objects.all()
<QuerySet [<Question: Question object (1)>]>

等一下。 <Question: Question object (1)> 不是这个对象的有用表示。 让我们通过编辑 Question 模型(在 polls/models.py 文件中)并将 __str__() 方法添加到 Question 和 [ X135X]:

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 自动生成的管理中使用了对象的表示。

让我们也为这个模型添加一个自定义方法:

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 datetimefrom django.utils import timezone,以引用 Python 的标准 datetime 模块和 django.utils.timezone 中与 Django 的时区相关的实用程序,分别。 如果您不熟悉 Python 中的时区处理,您可以在 时区支持文档 中了解更多信息。

保存这些更改并通过再次运行 python manage.py shell 来启动一个新的 Python 交互式 shell:

>>> from polls.models import Choice, Question

# Make sure our __str__() addition worked.
>>> Question.objects.all()
<QuerySet [<Question: What's up?>]>

# Django provides a rich database lookup API that's entirely driven by
# keyword arguments.
>>> Question.objects.filter(id=1)
<QuerySet [<Question: What's up?>]>
>>> Question.objects.filter(question_text__startswith='What')
<QuerySet [<Question: What's up?>]>

# Get the question that was published this year.
>>> from django.utils import timezone
>>> current_year = timezone.now().year
>>> Question.objects.get(pub_date__year=current_year)
<Question: What's up?>

# Request an ID that doesn't exist, this will raise an exception.
>>> Question.objects.get(id=2)
Traceback (most recent call last):
    ...
DoesNotExist: Question matching query does not exist.

# Lookup by a primary key is the most common case, so Django provides a
# shortcut for primary-key exact lookups.
# The following is identical to Question.objects.get(id=1).
>>> Question.objects.get(pk=1)
<Question: What's up?>

# Make sure our custom method worked.
>>> q = Question.objects.get(pk=1)
>>> q.was_published_recently()
True

# Give the Question a couple of Choices. The create call constructs a new
# Choice object, does the INSERT statement, adds the choice to the set
# of available choices and returns the new Choice object. Django creates
# a set to hold the "other side" of a ForeignKey relation
# (e.g. a question's choice) which can be accessed via the API.
>>> q = Question.objects.get(pk=1)

# Display any choices from the related object set -- none so far.
>>> q.choice_set.all()
<QuerySet []>

# Create three 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 objects have API access to their related Question objects.
>>> c.question
<Question: What's up?>

# And vice versa: Question objects get access to Choice objects.
>>> 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.
# Find all Choices for any question whose pub_date is in this year
# (reusing the 'current_year' variable we created above).
>>> Choice.objects.filter(question__pub_date__year=current_year)
<QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]>

# Let's delete one of the choices. Use delete() for that.
>>> c = q.choice_set.filter(choice_text__startswith='Just hacking')
>>> c.delete()

有关模型关系的更多信息,请参阅 访问相关对象 。 有关如何使用双下划线通过 API 执行字段查找的更多信息,请参阅 字段查找 。 有关数据库 API 的完整详细信息,请参阅我们的 数据库 API 参考


介绍 Django 管理页面

设计哲学

为您的员工或客户生成管理站点以添加、更改和删除内容是一项不需要太多创造力的乏味工作。 出于这个原因,Django 完全自动地为模型创建管理界面。

Django 是在新闻编辑室环境中编写的,“内容发布者”和“公共”站点之间有非常明确的区别。 站点管理员使用该系统添加新闻报道、事件、体育比分等,并将这些内容显示在公共站点上。 Django 解决了创建统一界面供站点管理员编辑内容的问题。

管理员不打算供站点访问者使用。 它适用于站点管理员。


创建一个管理员账号

首先,我们需要创建一个可以登录管理站点的用户。 运行以下命令:

键入你想要使用的用户名,然后按下回车键:

Username: admin

然后提示你输入想要使用的邮件地址:

Email address: admin@example.com

最后一步是输入您的密码。 您将被要求输入密码两次,第二次作为对第一次的确认。

Password: **********
Password (again): *********
Superuser created successfully.

启动开发服务器

Django 管理站点默认处于激活状态。 让我们启动开发服务器并探索它。

如果开发服务器未启动,用以下命令启动它:

现在,打开 Web 浏览器并转到本地域上的“/admin/”——例如,http://127.0.0.1:8000/admin/。 您应该会看到管理员的登录屏幕:

Django admin login screen 由于 translation 默认打开,登录屏幕可能会以您自己的语言显示,这取决于您的浏览器设置以及 Django 是否有该语言的翻译。


进入管理站点页面

现在,尝试使用您在上一步中创建的超级用户帐户登录。 您应该会看到 Django 管理索引页面:

Django admin index page 您应该会看到几种类型的可编辑内容:组和用户。 它们由 django.contrib.auth 提供,Django 提供的身份验证框架。


向管理页面中加入投票应用

但是我们的投票应用程序在哪里? 它不会显示在管理索引页面上。

只需要做一件事:我们需要告诉管理员 Question 对象有一个管理界面。 为此,请打开 polls/admin.py 文件,并将其编辑为如下所示:

polls/admin.py

from django.contrib import admin

from .models import Question

admin.site.register(Question)

体验便捷的管理功能

现在我们已经注册了 Question,Django 知道它应该显示在管理索引页面上:

Django admin index page, now with polls displayed 点击“问题”。 现在您在“更改列表”页面上提问。 此页面显示数据库中的所有问题,并让您选择一个进行更改。 有“怎么了?” 我们之前创建的问题:

Polls change list page 点击“怎么了?” 编辑它的问题:

Editing form for question object 注意事项:

  • 该表单是从 Question 模型自动生成的。
  • 不同的模型字段类型(DateTimeFieldCharField)对应于适当的 HTML 输入小部件。 每种类型的字段都知道如何在 Django 管理中显示自己。
  • 每个 DateTimeField 都有免费的 JavaScript 快捷方式。 日期有一个“今天”快捷方式和日历弹出窗口,时间有一个“现在”快捷方式和一个列出常用时间的方便弹出窗口。

页面的底部提供了几个选项:

  • 保存 - 保存更改并返回到此类对象的更改列表页面。
  • 保存并继续编辑 - 保存更改并重新加载此对象的管理页面。
  • 保存并添加另一个 - 保存更改并为此类对象加载新的空白表单。
  • 删除 - 显示删除确认页面。

如果“发布日期”的值与您在教程1中创建问题的时间不匹配,则可能意味着您忘记为设置正确的值:setting:`TIME_ZONE` 设置。 更改它,重新加载页面并检查是否出现了正确的值。

通过单击“今天”和“现在”快捷方式更改“发布日期”。 然后点击“保存并继续编辑”。 然后点击右上角的“历史”。 您将看到一个页面,其中列出了通过 Django 管理员对该对象所做的所有更改,以及进行更改的人员的时间戳和用户名:

History page for question object 当您熟悉模型 API 并熟悉管理站点后,请阅读本教程的 第 3 部分 ,了解如何向我们的投票应用程序添加更多视图。