编写第一个Django应用,第二部分
本教程从第一部分结束的地方开始。我们将设置数据库,创建您的第一个模型,并快速介绍 Django 自动生成的管理站点。
数据库设置
- 现在,打开
mysite/settings.py
。这是一个普通的 Python 模块,其中有代表 Django 设置的模块级变量。 - 默认情况下,配置使用 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
应该是该文件的完整绝对路径,包括文件名。默认值将文件存储在您的项目目录中。BASE_DIR / 'db.sqlite3'
- ENGINE 可以是
-
如果您不使用 SQLite 作为数据库,则必须添加其他设置,例如 USER、PASSWORD。有关更多详细信息,请参阅 的参考文档。HOST DATABASES
-
对于 SQLite 以外的数据库
如果您使用的是 SQLite 以外的数据库,请确保此时已创建数据库。在数据库的交互式提示中使用“ ”执行此操作。
CREATE DATABASE database_name
;
还要确保提供的数据库用户mysite/settings.py
具有“创建数据库”权限。这允许自动创建 测试数据库,后面的教程中会用到。
如果您使用 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
– 管理静态文件的框架。
-
这些应用程序默认包含在内,以方便常见情况。
-
不过,其中一些应用程序至少使用一个数据库表,因此我们需要先在数据库中创建表,然后才能使用它们。为此,请运行以下命令:
-
该migrate命令会查看
INSTALLED_APPS
设置并根据mysite/settings.py
文件中的数据库设置和应用程序附带的数据库迁移(我们稍后会介绍)创建任何必要的数据库表。您将看到它应用的每个迁移的消息。如果您有兴趣,请运行数据库的命令行客户端并输入\dt(PostgreSQL)、(MariaDB、MySQL)、 (SQLite)或(Oracle)以显示 Django 创建的表。SHOW TABLES;.tablesSELECT TABLE_NAME FROM USER_TABLES;
-
对于极简主义者来说
正如我们上面所说,默认应用程序包含在常见情况下,但并非每个人都需要它们。如果您不需要其中的任何或全部,请在
INSTALLED_APPS
运行之前随意注释掉或删除相应的行migrate
。该migrate
命令将仅针对中的应用程序运行迁移INSTALLED_APPS
。
创建模型
- 现在我们将定义您的模型 - 本质上是您的数据库布局,带有附加的元数据。
-
介绍
模型是有关数据的唯一、权威的信息来源。它包含您存储的数据的基本字段和行为。Django 遵循DRY 原则。目标是在一个地方定义您的数据模型并自动从中派生内容。
这包括迁移 - 与 Ruby On Rails 不同,例如,迁移完全来自您的模型文件,并且本质上是 Django 可以滚动更新数据库模式以匹配您当前模型的历史记录。 -
在我们的投票应用中,我们将创建两个模型:
Question
和Choice
。AQuestion
有一个问题和一个发布日期。AChoice
有两个字段:选项文本和投票结果。每个字段都Choice
与一个相关联Question
。 -
这些概念由 Python 类表示。编辑 polls/models.py文件,使其看起来像这样:
-
Example
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_text
或pub_date
)是字段的名称,采用机器友好的格式。您将在Python
代码中使用此值,并且您的数据库将使用它作为列名。 -
您可以使用可选的第一个位置参数来
Field
指定一个人类可读的名称。这在Django
的几个内省部分中使用,并且它兼作文档。如果未提供此字段,Django
将使用机器可读的名称。在此示例中,我们仅为 定义了一个人类可读的名称Question.pub_date
。对于此模型中的所有其他字段,字段的机器可读名称就足以作为其人类可读的名称。 -
有些
Field
类有必需的参数。CharField
例如,要求您为其提供max_length
。这不仅用于数据库模式,还用于验证,我们很快就会看到。 -
AField
还可以有各种可选参数;在本例中,我们将default
的值 设置votes
为 0。 -
最后,请注意使用 定义了一个关系 ForeignKey。这告诉
Django
每个 都Choice
与一个 相关联Question
。Django
支持所有常见的数据库关系:多对一、多对多和一对一。
激活模型
- 这一小段模型代码为 Django 提供了大量信息。有了它,Django 能够:
- 为此应用程序创建数据库模式(语句)。
CREATE TABLE
-Question
创建一个用于访问和对象的Python 数据库访问API Choice
。 - 但首先我们需要告诉我们的项目该
polls
应用程序已安装。 -
注意
Django 应用程序是“可插入的”:您可以在多个项目中使用一个应用程序,并且可以分发应用程序,因为它们不必与给定的 Django 安装绑定。
-
要将应用程序包含到我们的项目中,我们需要在设置中添加对其配置类的引用
INSTALLED_APPS
。该类PollsConfig
位于文件中polls/apps.py
,因此其虚线路径为'polls.apps.PollsConfig'
。编辑mysite/settings.py
文件并将该虚线路径添加到INSTALLED_APPS
设置中。它看起来像这样: -
现在 Django 知道要包含该polls应用程序。让我们运行另一个命令:
-
您应该看到类似以下内容的内容:
-
通过运行
makemigrations
,你告诉 Django 你已经对模型做了一些更改(在本例中,你做了新的模型),并且你想将这些更改存储为迁移。 -
迁移是 Django 存储模型(以及数据库架构)更改的方式 - 它们是磁盘上的文件。如果您愿意,您可以读取新模型的迁移;它是文件
polls/migrations/0001_initial.py
。别担心,您不需要每次 Django 进行迁移时都读取它们,但它们被设计为可人工编辑,以防您想手动调整 Django 更改事物的方式。 -
有一个命令可以为您运行迁移并自动管理您的数据库架构 - 该命令称为
migrate
,我们稍后会讲到它 - 但首先,让我们看看该迁移将运行哪些 SQL。该sqlmigrate
命令获取迁移名称并返回其 SQL: -
您应该会看到类似以下内容的内容(我们已重新格式化以提高可读性):
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
) 和模型的小写名称 –question
和 来自动生成的choice
。(您可以覆盖此行为。) - 主键 (ID) 会自动添加。(您也可以覆盖此设置。)
- 按照惯例,Django 会将
"_id"
外键字段名称附加到该字段上。(是的,您也可以覆盖这一点。) - 外键关系通过约束明确化 。不必担心各个部分;它告诉
PostgreSQL
在事务结束前不要强制执行外键。FOREIGN KEYDEFERRABLE
- 它根据您使用的数据库量身定制,因此会自动为您处理特定于数据库的字段类型,例如
auto_increment
(MySQL)、(PostgreSQL) 或(SQLite)。字段名称的引号也是如此 - 例如,使用双引号或单引号。bigint PRIMARY KEY GENERATED BY DEFAULT AS IDENTITYinteger primary key autoincrement
- 该
sqlmigrate
命令实际上不会在您的数据库上运行迁移 - 相反,它会将其打印到屏幕上,以便您可以看到 SQL Django 认为需要什么。这对于检查 Django 将要做什么或您的数据库管理员是否需要 SQL 脚本进行更改非常有用。
- 确切的输出将根据您使用的数据库而有所不同。上面的示例是为
-
如果您有兴趣,您也可以运行 ;这会检查您的项目中是否存在任何问题,而无需进行迁移或接触数据库。
python manage.py check
-
现在,
migrate
再次运行以在数据库中创建这些模型表: -
该
migrate
命令获取所有尚未应用的迁移(Django 使用数据库中名为 的特殊表来跟踪哪些迁移已应用django_migrations
)并针对数据库运行它们 - 本质上,将您对模型所做的更改与数据库中的模式同步。 -
迁移功能非常强大,可以让您在开发项目的过程中随时间更改模型,而无需删除数据库或表并创建新的数据库或表 - 它专门用于实时升级数据库,而不会丢失数据。我们将在本教程的后面部分更深入地介绍它们,但现在,请记住进行模型更改的三步指南:
- 更改你的模型(在
models.py
)。 - 运行以针对这些更改创建迁移
python manage.py makemigrations
- 运行以将这些更改应用到数据库。
python manage.py migrate
- 更改你的模型(在
-
之所以有单独的命令来制作和应用迁移,是因为您将把迁移提交到版本控制系统并将其与您的应用程序一起发送;它们不仅使您的开发更容易,而且还可供其他开发人员和生产中使用。
-
阅读django-admin 文档以获取有关该实用程序功能的完整信息
manage.py
。
使用 API
- 现在,让我们进入交互式 Python shell,试用一下 Django 提供的免费 API。要调用 Python shell,请使用以下命令:
- 我们使用它而不是简单地输入“python”,因为manage.py 设置了DJANGO_SETTINGS_MODULE环境变量,它为 Django 提供了文件的 Python 导入路径mysite/settings.py。
-
进入 shell 后,探索数据库 API:
>>> from polls.models import Choice, Question # 导入我们刚刚编写的模型类。 # 系统中还没有任何问题。 >>> Question.objects.all() <QuerySet []> # 创建一个新问题。 # 默认设置文件中启用了对时区的支持,因此 # Django期望pub_date有一个带有tzinfo的日期时间。使用 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(2012, 2, 26, 13, 0, 0, 775217, 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)>Questionpolls/models.py__str__()QuestionChoice
-
向模型中添加
__str__()
方法很重要,这不仅是为了您在处理交互式提示时方便,还因为对象的表示会在整个 Django 自动生成的管理中使用。 -
我们还向该模型添加一个自定义方法:
-
请注意添加了和,分别用于引用 Python 的标准模块和 Django 的时区相关实用程序。如果您不熟悉 Python 中的时区处理,您可以在时区支持文档中了解更多信息。
import datetimefrom django.utils import timezonedatetimedjango.utils.timezone
-
保存这些更改并通过再次运行来启动新的 Python 交互式
shell :python manage.py 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 (defined as "choice_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 Admin
-
背景
为您的员工或客户创建管理网站以添加、更改和删除内容是一项繁琐的工作,不需要太多创造力。因此,Django 完全自动化了模型管理界面的创建。
Django 是在新闻编辑室环境中编写的,其中“内容发布者”和“公共”站点之间有非常明确的区分。站点管理员使用该系统添加新闻报道、事件、体育比分等,这些内容显示在公共站点上。Django 解决了为站点管理员创建统一的内容编辑界面的问题。
管理员界面不适合网站访问者使用。它供网站管理员使用。
创建管理员用户
-
首先,我们需要创建一个可以登录管理站点的用户。运行以下命令:
-
输入您想要的用户名并按回车键。
-
然后系统将提示您输入所需的电子邮件地址:
-
最后一步是输入密码。系统会要求您输入两次密码,第二次输入是为了确认第一次输入的密码。
启动开发服务器
- Django 管理站点默认已激活。让我们启动开发服务器并进行探索。
-
如果服务器没有运行,请按如下方式启动:
-
现在,打开 Web 浏览器并转到本地域上的“/admin/” - 例如
http://127.0.0.1:8000/admin/
。您应该看到管理员的登录屏幕: -
由于默认情况下翻译是启用的,如果您设置LANGUAGE_CODE,登录屏幕将以给定的语言显示(如果 Django 有适当的翻译)。
进入管理站点
- 现在,尝试使用上一步中创建的超级用户帐户登录。您应该看到 Django 管理索引页:
- 您应该会看到几种可编辑的内容:组和用户。它们由
django.contrib.authDjango
提供的身份验证框架提供。
让投票应用可以在管理界面中修改
- 但是我们的投票应用在哪里呢?它没有显示在管理主页上。
- 只剩一件事要做:我们需要告诉管理员
Question
对象有一个管理界面。为此,打开文件polls/admin.py
,并将其编辑为如下所示:
探索免费管理功能
- 现在我们已经注册了
Question
,Django 知道它应该显示在管理索引页上: - 点击“问题”。现在您进入了问题的“更改列表”页面。此页面显示数据库中的所有问题,并允许您选择一个进行更改。这是我们之前创建的“有什么事?”问题:
- 点击“有什么事?”问题进行编辑:
需要注意的是:
- 该表单是根据
Question
模型自动生成的。 - 不同的模型字段类型(
DateTimeField
,CharField
)对应相应的 HTML 输入小部件。每种类型的字段都知道如何将自己显示在 Django 管理面板中。 - 每个
DateTimeField
都有免费的 JavaScript 快捷方式。日期有“今天”快捷方式和日历弹出窗口,时间有“现在”快捷方式和列出常用输入时间的便捷弹出窗口。
- 页面底部提供了几个选项:
- - 保存 – 保存更改并返回此类型对象的更改列表页面。
- - 保存并继续编辑 – 保存更改并重新加载此对象的管理页面。
- - 保存并添加另一个 – 保存更改并为此类对象加载新的空白表单。
- - 删除 – 显示删除确认页面。
-
如果“发布日期”的值与你在教程 1中创建问题的时间不匹配,则可能意味着你忘记为该TIME_ZONE设置设置正确的值。更改它,重新加载页面并检查是否显示正确的值。
-
通过单击“今天”和“现在”快捷方式更改“发布日期”。然后单击“保存并继续编辑”。然后单击右上角的“历史记录”。您将看到一个页面,其中列出了通过 Django 管理员对此对象所做的所有更改,以及进行更改的人的时间戳和用户名: