Skip to main content

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

本教程从 教程1 离开时开始。我们将设置数据库,创建您的第一个模型,并快速介绍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 应该是该文件的完整绝对路径,包括文件名。默认值 os.path.join(BASE_DIR, 'db.sqlite3') 将文件存储在项目目录中。

如果不使用SQLite作为数据库,则必须添加其他设置,如 USERPASSWORDHOST。有关更多详细信息,请参阅 DATABASES 的参考文档。

对于除SQLite之外的数据库

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

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

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

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

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

默认情况下,INSTALLED_APPS 包含以下应用程序,所有这些都来自Django:

默认情况下,包括这些应用程序是为了方便常见的情况。

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

$ python manage.py migrate

migrate 命令查看 INSTALLED_APPS 设置,并根据您的 mysite/settings.py 文件中的数据库设置和应用程序随附的数据库迁移(我们将在后面讨论)创建任何必要的数据库表。您会看到每个适用的迁移的讯息。如果您感兴趣,请为数据库运行命令行客户端,然后键入 \dt (PostgreSQL),SHOW TABLES; (MySQL),.schema (SQLite)或 SELECT TABLE_NAME FROM USER_TABLES; (Oracle)以显示Django创建的表。

对于极简主义者

就像我们上面说的,默认应用程序包括在普通的情况下,但不是每个人都需要它们。如果你不需要任何或所有的,在运行 migrate 之前,请随意注释掉或删除适当的行从 INSTALLED_APPSmigrate 命令将仅对 INSTALLED_APPS 中的应用程序运行迁移。

创建模型

现在,我们将定义您的模型 - 实际上,您的数据库布局,与额外的元数据。

哲学

模型是关于你的数据的唯一,确定的真理来源。它包含您存储的数据的基本字段和行为。 Django遵循 干性原理。目标是在一个地方定义您的数据模型,并从中自动导出数据。

这包括迁移 - 例如,与Ruby On Rails不同,迁移完全从您的模型文件派生,并且本质上只是一个历史,Django可以滚动更新您的数据库模式以匹配当前模型。

在我们的简单投票应用程序中,我们将创建两个模型:QuestionChoiceQuestion 有问题和出版日期。 Choice 有两个字段:选择的文本和投票计数。每个 ChoiceQuestion 相关联。

这些概念由简单的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安装。

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

$ 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你已经对你的模型做了一些修改(在这种情况下,你已经做了新的),并且你希望更改被存储为 migration

迁移是Django如何存储对模型的更改(以及您的数据库模式) - 它们只是磁盘上的文件。如果您愿意,可以阅读新模型的迁移;它是文件 polls/migrations/0001_initial.py。不要担心,你不应该每次Django做一个阅读它们,但他们的设计是人类可编辑的,如果你想手动调整Django如何改变事情。

有一个命令将为您运行迁移并自动管理您的数据库模式 - 这就是所谓的 migrate,我们会在稍后介绍 - 但首先,让我们看看迁移将运行什么SQL。 sqlmigrate 命令使用迁移名称并返回其SQL:

$ python manage.py sqlmigrate polls 0001

您应该看到类似以下内容(我们已经重新格式化它为了可读性):

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)。对于字段名称的引用也是如此 - 例如,使用双引号或单引号。

  • sqlmigrate 命令实际上不在数据库上运行迁移 - 它只是将其打印到屏幕上,以便您可以看到SQL Django认为需要什么。它有助于检查Django将要执行的操作,或者如果数据库管理员需要SQL脚本来进行更改。

如果你有兴趣,你也可以运行 python manage.py check;这将检查您的项目中的任何问题,而不进行迁移或触摸数据库。

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

$ python manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, polls, sessions
Running migrations:
  Rendering model states... DONE
  Applying polls.0001_initial... OK

migrate 命令采用所有尚未应用的迁移(Django跟踪哪些迁移使用数据库中称为 django_migrations 的特殊表应用),并对数据库运行它们 - 实质上,将您对模型所做的更改与模式同步在数据库中。

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

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

阅读 django-admin文档 有关 manage.py 实用程序可以做什么的完整信息。

使用API

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

$ python manage.py shell

我们使用这个而不是简单地键入“python”,因为 manage.py 设置 DJANGO_SETTINGS_MODULE 环境变量,这给Django Python导入路径到您的 mysite/settings.py 文件。

绕过manage.py

如果你不想使用 manage.py,没有问题。只需将 DJANGO_SETTINGS_MODULE 环境变量设置为 mysite.settings,启动一个纯Python shell,并设置Django:

>>> import django
>>> django.setup()

如果这引发了 AttributeError,你可能使用的Django版本不匹配本教程版本。您将需要切换到较旧的教程或较新的Django版本。

您必须从 manage.py 所在的同一目录运行 python,或确保目录在Python路径上,以便 import mysite 工作。

有关所有这些的更多信息,请参阅 django-admin文档

一旦你在shell中,探索 数据库API:

>>> from polls.models import Question, Choice   # 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. 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.
>>> 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>]>

等一下。 <Question: Question object> 完全是这个对象的无益表示。让我们通过编辑 Question 模型(在 polls/models.py 文件中)并在 QuestionChoice 中添加 __str__() 方法来解决这个问题:

polls/models.py
from django.db import models
from django.utils.encoding import python_2_unicode_compatible

@python_2_unicode_compatible  # only if you need to support Python 2
class Question(models.Model):
    # ...
    def __str__(self):
        return self.question_text

@python_2_unicode_compatible  # only if you need to support Python 2
class Choice(models.Model):
    # ...
    def __str__(self):
        return self.choice_text

__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)

注意添加 import datetimefrom django.utils import timezone,分别引用Python的标准 datetime 模块和Django在 django.utils.timezone 中的与时区相关的实用程序。如果你不熟悉Python中的时区处理,你可以在 时区支持文档 中了解更多。

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

>>> from polls.models import Question, Choice

# 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解决了为网站管理员创建统一界面以编辑内容的问题。

管理员不打算供网站访问者使用。这是为网站经理。

创建管理用户

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

$ python manage.py createsuperuser

输入所需的用户名,然后按Enter键。

Username: admin

然后系统会提示您输入所需的电子邮件地址:

Email address: admin@example.com

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

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

启动开发服务器

默认情况下,Django管理网站已激活。让我们开始开发服务器并探索它。

如果服务器没有运行,它就像这样:

$ python manage.py runserver

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

Django admin login screen

由于默认情况下启用了 翻译,因此登录屏幕可能会以您自己的语言显示,具体取决于浏览器的设置以及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

点击“What’s up?”问题编辑它:

Editing form for question object

注意事项:

  • 该表单是从 Question 模型自动生成的。

  • 不同的模型字段类型(DateTimeFieldCharField)对应于适当的HTML输入小部件。每种类型的字段都知道如何在Django管理中显示自己。

  • 每个 DateTimeField 获得免费的JavaScript快捷方式。日期获得一个“今天”快捷方式和日历弹出窗口,并且时间获得一个“现在”快捷方式和一个方便的弹出列出常见的输入时间。

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

  • 保存 - 保存更改并返回到此类型对象的更改列表页面。

  • 保存并继续编辑 - 保存更改并重新加载此对象的管理页面。

  • 保存并添加另一个 - 保存更改并为此类型的对象加载新的空白表单。

  • 删除 - 显示删除确认页面。

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

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

History page for question object

当您熟悉模型API并熟悉管理网站时,请阅读 part 3 of this tutorial 了解如何向我们的投票应用添加更多视图。