Django 2.0.1 官方文档翻译: 编写你的第一个 Django app,第二部分(Page 7)

编写你的第一个 Django app,第二部分(Page 7)转载请注明链接地址

本教程上接前面的教程。我们会配置数据,创建你的第一个 model,并对Django 自动生成的 admin 站点进行快速的介绍。

数据库设置

现在,打开 mysite/settings.py。它是一个带有模块级变量的普通 Python 模块,也是 Django 的配置文件。
默认情况下,配置中使用的是 SQLite,如果你是一个数据库新手,或者你只是对 Django 刚兴趣儿想尝试一下,这是最简单的选择。Python 默认包含 SQLite,所以我们不需要安装任何东西来支持数据库。但是,当你开发你的第一个真正的项目,你希望使用像PostgreSQL一样有更易于扩展的数据库,避免为切换数据库切换头疼。

如果你希望使用其他数据库,请安装合适的数据库绑定(就是像 pymysql 一样用于链接数据库的 python 模块),并更改数据库“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 作为你的数据库,像 USER,PASSWORD 和 HOST 这些额外设置必须被添加。更多信息,请参考数据库文档这里少一个链接

除 SQLite 以外的其他数据库
如果你使用了 SQLite 之外的数据库,确保你已经创建了一个数据库,在数据库的交互式解释器里用“CREATE DATABASE database_name;”完成。
请确保配置在mysite/settings.py中的数据库用户有“create database”的权限,这样在后面的教程里需要的时候,会自动创建一个测试数据库(这里少一个链接)
如果你使用的是 SQLite,你不需要提前创建任何东西 —— 数据库文件会在需要的时候自动创建。

当你编辑 mysite/settings.py 的请将 TIME_ZONE(这里少一个链接) 设置为你的时区。

同时,注意注意配置文件顶部的 INSTALLED_APPS(这里少一个链接) 设置,它包含了在本项目实例中所有被激活的 django app的名字。app可以被用于多个项目,你给可以打包和分发它给其他人在他们的项目里使用。

默认情况下,INSTALLED_APPS(这里少一个链接)包含下面几个app,他们都是 Django 自带的:

默认包含的这些app为一些常见的情况提供方便。

其中一些app需要使用至少一个数据库表,因此,在我们使用它们之前,我们需要在数据库中创建表。我们可以使用下面的命令来做到:

$ python manage.py migrate

migrate(这里少一个链接) 命令会依据 mysite/settings.py中的数据库配置 和 INSTALLED_APPS(这里少一个链接) 的设置创建需要的数据库表,将app的的数据迁移到数据库(这个外面稍后在讨论)。每次app迁移你都可以看到一条消息。如果你感兴趣,运行数据库的命令行客户端,输入\dt (PostgreSQL), SHOW TABLES; (MySQL), .schema (SQLite), or SELECT TABLE_NAME FROM USER_TABLES; (Oracle) 来显示 Django 显示数据库表。(文档中这里的标点似乎有些问题,我理解的是每个圆括号中的内容对应它前面的命令)。

对极简主义者来说
就像我们上面说到的,app默认包含一些普通实例(就像 auth,user),但不是每个人都需要用到它们。如果你用不到他们,可以在运行 migrate命令前在INSTALLED_APPS中注释或删除对应的行。 migrate命令只会对INSTALLED_APPS中app 进行迁移。

创建模型(models)

现在我们开始定义你的模型 —— 本质上是带有额外元数据的数据库布局。

设计哲学
模型是唯一可靠的数据来源。它包含你存储的数据的必要字段和行为。Django遵循DRY原则(这里少一个链接)。 我们的目标是在一个地方定义你的数据模型并从它那获取数据。
这里包含的迁移 —— 和 Ruby On Rails 不同,例如:迁移完全源自你的 模型文件,本质是它只是一个历史,Django 可以通过更新的数据库模式(database schema)来匹配你的当前模型。

在我们简单的 poll app中,我们创建了两个模型,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_text pub_date)的名字就是字段的名字,是对机器友好的格式。你的 Python 代码中会使用这个值,你的数据库会将其作为列的名字。

你可以使用 Field(这里少一个链接)可选的第一个位置参数来将其命名为一个人们更容易理解的名字。这在Django 的一些内省部分使用过,同时它也可以做为注释文档。如果这个字段未指定,Django 会使用一个让机器易读的名字。在这个例子中,我们只给 Question.pub_date 定义一个让人类易读的名字。这个模型中的其他所有字段,使用让机器易读的名字已经足以让人也易读。

一些Field(这里少一个链接) 需要指定参数,例如:CharField(这里少一个链接)需要你指定 max_length(这里少一个链接)。这不仅仅只用于数据库模式(database schema),后面我们也用于验证中。

一个Field(这里少一个链接)有很多可选的参数,这个例子中,我们设置 votes默认值这里少一个链接)为0.

最后,注意使用 ForeignKey 时有一个关系被定义,它告诉 Django Choice中的每一条记录都对应 Question中的一条记录。Django 支持所有常用的数据库关系:一对多,多对多,一对一。(many-to-one, many-to-many, and one-to-one.)

激活模型

一小部分代码涉及了Django 的很多信息。通过它,Django 可以:

  • 在这个app中创建数据库模式(data schema)(CREATE TABLE语句)
  • 创建用于访问 QuestionChoice 对象的 Python数据库链接 API

但是我们先告诉我们的项目 polls app已经被安装。

设计哲学
Django app是可插拔的: 你可以在多个项目中使用同一个app,也可以发布app,因为他们不需要绑定到一个给定的 Django 安装中。

我们项目中包含的app,我们需要在INSTALLED_APPS(这里少一个链接) 设置中添加一个到配置类的参考。PollsConfig 类在polls/apps.py中,所以它的引用路径是:'polls.apps.PollsConfig'。编辑mysite/settings.py文件,添加app的路径到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 app。我们来运行另外一个命令:

$ 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,你对你的模型做了一些更改(这个例子中,你创建了一个新的),并且你希望这个更改可以像迁移(migraion)一样被存储。

迁移如何更改你的 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.
  • 数据库表名会结合app(polls)中的名字和模型的小写名字的自动生成,—— questionchoice 。(你可以覆盖该行为)
  • 主键(IDs)会被自动添加。(这个行为你也可以覆盖)
  • 按照惯例,Django 会在外键字段的名字中追加 “_id”。(这个行为你也可以覆盖)
  • 外键关系根据外键约束来明确创建。不要担心 DEFERRABLE 的部分,那只是告诉 PostgreSQL 在通信结束前不要强制使用外键。
  • 它是针对你使用的数据库定制的,像 auto_increment (MySQL), serial (PostgreSQL), or integer primary key autoincrement (SQLite) 这些特定的数据库字段类型会为你自动处理。字段名称的引用也是如此 —— 例如,使用说引号或者单引号。
  • 实际上 sqlmigrate 命令并不会在你的数据库上执行迁移 —— 它只是将你可以看到的 Django 认为需要执行的 SQL 打印到屏幕。它用来检查 Django 会如何做 或者 你是否需要更改 SQL 脚本的数据库管理员权限。

如果你感兴趣,你可还可以运行 python manage.py check;这可以在不做迁移或链接数据库的情况下检查问题。

现在再次运行 mingrate 来在你的数据库中创建这些模型。

$ 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

mingrate 命令会执行所有尚未被应用的迁移(Django 在数据库中使用一个叫 django_migrations 的特殊表来追踪哪些迁移被应用),并在数据库中运行他们 —— 本质上,使用数据库中的模式来同步更改到你的模型中。

迁移非常强大,可以让你随着时间推移更改你的模型,当你开发你的项目时,不需要删除你的数据库或者表后 再创建新的 —— 他们专门在不丢失数据库的情况下升级你的数据库。我们会在后面的教程中深入研究它,而现在,记住创建模型变更的散步操作:

  • 更改你的模型 (在 models.py 中)
  • 运行 python manage.py makemigrations 为这些变更创建迁移
  • 运行 python manage.py migrate 将这些变更应用到数据库

分离创建迁移和应用迁移命令的原因是你会提交迁移到你的版本控制系统并把它们和你的 app 一起运送;这不仅仅使你的开发更容易,也供其他开发人员和生产人员使用。

阅读 django-admin documentation(这里少一个链接) 了解 manage.py工具可以做什么 的完整信息

玩转 API

现在,我们进入交互式 Python shell,玩转 Django 提供的API,使用下面的命令,进入 python shell:

$ python manage.py shell

我们用上面的命令替代简单的输入“python”,是因为 manage.py 会设置DJANGO_SETTINGS_MODULE(这里少一个链接) 环境变量,这样 Python 会将 mysite/settings.py文件导入到你的 Django (其实就是进入一个快速有当前项目环境变量的 shell 中)。

不使用 manage.py
如果你不想使用 manage.py,没有问题,只需要在 mysite.settings中设置 DJANGO_SETTINGS_MODULE(这里少一个链接) ,启动一个简单的 Python shell 并安装 Django 即可:

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

如果这里报告 AttributeError 异常,你可能使用了一个不适用当前版本文档的 django。你需要切换到老版本的文档或新版本的 Django。
你必须在 manage.py 的同级目录中运行 python,或者确保目录在 Python 路径上(python 的 sys.path 中),这样 import mysite 就可以工作了。
更多信息,请查阅 django-admin documentation(这里少一个链接)

当你进入 shell 后,你就可以研究 database 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.
>>> 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文件中) 模型,在 QuestionChoice 中都加入 __str__()(这里少一个链接)方法。

# 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 自动生成admin的过程。

注意这些普通的 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.util 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()

更多关于模型关系的信息,请查看 Accessing related objects(这里少一个链接)。更多关于如何通过 API 使用双下划线查找字段,请查看 字段查找(这里少一个了链接)。数据库 API 的详细信息可以查看 数据库 API 参考(这里少一个链接)

介绍 Django admin

设计哲学
为你的员工或客户生成有增加、更改、删除内容功能的管理站点的工作很单调,因为这不需要什么创造力。因为这个原因,Django完全为模型自动创建管理接口。
Django是在一个新闻编辑环境被写出的,在“内容发布”和“公共”站点之间做了非常明确的分离。站点管理者使用这个系统去添加新闻故事、事件、体育分数等,并且这些内容会被显示在公共站点上。Django 为了解决站点管理员编辑内容的问题创建了一个统计接口。
admin 不是让站点的游客使用的,它是为站点的管理者准备的。

创建一个 admin 用户

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

$ python manage.py createsuperuser

输入你想使用的用户名并回车:

Username: admin

然后提示你输入你想使用的 Email 地址:

Email address: admin@example.com

最后一步是输入你的密码。会提示你输入两次密码,两次输入的密码必须一样。

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

开始开发服务器

Django admin 站点默认是激活的。现在我们开始开发服务器并浏览它。
如果服务器没有运行,执行下面的命令来运行它:

$ python manage.py runserver

现在打开一个 web 浏览器,并打开本地域的 "/admin" —— 例如:http://127.0.0.1:8000/admin/. 你应该可以看到 admin 的登陆界面:

默认情况下 翻译(这里少一个链接) 是打开的,登陆界面可能会显示成你使用的语言,这取决于浏览器的设置 和 Django 是否有这种语言的翻译。

进入 admin 站点

现在,尝试使用超级用户登陆前面步骤中你创建的站点,你会看到 Django admin 的索引页:

你会看到几种可编辑的内容:groups 和 users。它们由 django.contrib.auth(这里少一个链接)提供,默认情况,认证框架会被 Django 加载。

使 poll app可以在 admin 中被修改

我们的 pollapp 在哪里呢?它并没有显示在 admin的索引页面。
我们只需要做一件事情:我们需要告诉 admin, Question 对象有一个 admin 接口。要做到这个,需要打开 polls/admin.py 文件,像下面一样编辑它:

# polls/admin.py
from django.contrib import admin

from .models import Question

admin.site.register(Question)

研究自由的 admin 功能

现在我们注册了 Question,django 知道了它应该被显示在 admin 索引页面上:

点击“Questions”。现在你就进入“change list”页面编辑问题了。这个页面显示数据库中的所有的问题,让你可以选择并修改。有一个我们前面创建的“what's up?”问题:

点击“what's up?”问题来编辑它:

这里有一些需要注意的内容:

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

  • 不同的模型字段类型(DateTimeField, CharField)对应相应的 HTML 输入控件。每一个字段类型都知道如何在 Django admin 中显示自己。

  • 每一个 DateTimeField 可以获取 JavaScript 快捷方式(这里的快捷方式感觉翻译成按钮更能好一些,原词是shortcut)。Dates 获取“Today”的快捷方式和日历弹出框,times 获取“Now”的快捷方式并弹出一个列出常用输入时间的弹出框。
    页面的底部给了你几个选项:

  • Save – 保存更改并返回此类型对象的更改列表页面

  • Save and continue editing – 保存更改并为这个对象重载admin 页面

  • Save and add another – 保存更改并加载一个新的,空表单的该类型的对象。

  • Delete – 显示一个删除确认页面。

如果 “Date published” 的值和你在 Tutorial 1中创建问题的时间不匹配,可能是你忘记了给 TIME_ZONE(这里少一个链接) 设置正确的值。修改它,重载页面后再检查,就可以看到正确的值了。

点击 “Today” 和 “Now”的快捷方式 可以修改“Date published”,然后点击 “Save and continue editing.” 在点击右上角”History“。你就可以通过Django admin看到一个列出所有对这个对象做的修改的页面,还带有做出这个修改该时间戳和用户名:

当你熟悉了模型 API,了解了 admin ,就可以学习 教程的第三部分(Page 8) ,学习如何在我们的 polls app中添加更多视图。

posted on 2018-01-24 13:06  R_e  阅读(2261)  评论(0编辑  收藏  举报

导航