Django官方教程翻译

Part2 数据库设置

2017-12-12  本文已影响0人  shark_tear

第一部分结束以后,我们来学习第二部分的数据库设置。这一章我们来学习怎么创建数据库模块(model)以及Django自动生成的管理站点。

数据库设置

我们先打开mysite/settings.py,这个模块就是Django中其他模块的配置参数所在的模块。默认情况下,Django使用SQLite数据库。如果你对数据库不熟悉,就只是想尝试一下Django,使用默认设置就可以了。如果你想用其他的生产数据库,例如MySQL、PostgreSQL等,可以自己去查找Django的官方文档。

编辑mysite/settings.py文件的时候,将TIME_ZONE设置为你自己的时间区域(中国默认的设置是Asia/Shanghai)。在文件的上面有一个INSTALLED_APPS设置,这个地方保存的是当前Django项目中所有需要使用到的应用名称。前面我们说到过,一个应用可以放在多个项目里,只需要将应用的目录放到对应的项目目录下,然后在这个参数里将应用的名称保存下来即可。这这个参数里还默认自带了6个应用,其作用分别是:

这些应用都是作为一个通用组件包含在每一个Django项目中,在后面我们学习了更多的Django知识以后,就可以来尝试自己定制或者修改这些组件,目前还是先用默认的组件。
上面这些组件中的部分组件要使用的话,需要在数据库中创建表,因此我们首先需要在数据库中将对应的表创建出来。然后才能使用它们。创建表的命令如下:
$ python manage.py migrate
migrate命令查看INSTALLED_APPS配置,然后根据你在mysite/settings.py里的设置以及应用里的迁移配置来创建需要的数据库(我们后面来介绍数据库表)。这个命令运行过程中,你会看到很多创建表的信息,如果你对这些表感兴趣的话,可以自己登录数据库查看创建的表(需要你自己对数据库有了解)。

创建模型(model)

现在来创建你的数据库模型——简单的说,就是你的数据库布局(或者说表结构),还有一些额外的信息。
在我们的投票应用中,我们会创建两个模型,QuestionChoice。模型Question会有一个代表问题的字段和一个表示发布日期的字段。Choice会有,一个表示选择的文本字段和一个表示投票数量的字段。每个Choice都是和一个Question进行关联的。
上面的这些概念都是通过Python类来表示,编辑polls/modes.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.modesl.Model。每个模型都有一堆类属性,模型里的每一种属性都代表一个数据库字段。
每一个字段都是由一个Field类实例来表示的,例如CharField表示字符串字段,DateTimeField表示日期时间。通过这种方式来告知Django每种字段在数据库中的数据类型。每个Field实例的名称(例如question_textpub_date)就是数据库中字段的名称,且通常是一个对机器友好的名称(英文表示)。你可以在你的Python代码中使用这个名称,而在数据库中会作为表字段的名称。
你可以使用一个Field实例的第一个可选位置参数,来作为用户阅读起来比较友好的名称(自己所使用的语言,例如一个中文名)。如果没有给出这个参数,默认就会使用前面的表字段名。在上面的例子里, 我们只给pub_date定义了这样一个参数(date published)。对于这个模型中的其他字段,都会使用默认的字段名称。

一些Field类有一些必须的参数,例如CharField,需要你给出一个max_length参数,用来表示这个字段的长度,这个值不光用在数据库表里面,也会用在验证上,后面我们会看到这部分的内容。

一个Field也可以拥有多个可选参数,在上面的例子里,我们看到votes字段的默认值设置为0(default=0)。最后,我们看到使用Foreign Key定义了一个关联关系,这告诉Django,每一个Choice和一个Question进行关联。Django支持所有的通用数据库关联:多对一、多对多、一对一。

激活模块

上面那一小部分代码给出了大量的信息,根据这些代码,Django可以做:

但是我们首先需要告诉我们的项目,我们将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',
]

现在你的项目就知道已经包含了这个应用,下面来运行另外一个命令:
$ 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你需要变更一些修改到你的模型里(这个例子里是创建一个新的数据库),并且这些修改会作为一个迁移保存起来。

迁移的意思是,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;

注意下面的内容:

如果你感兴趣,可以运行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),然后在你的数据库上运行这些命令。实际上就是将你在模型上的修改同步到你的数据库里。

迁移这个功能是非常强大的,让你在开发项目的时候随着时间的改变来修改你的模型,而不需要删除你的数据库、表或者新建一个新的数据库或者表。它专门用来升级你的数据库而不会丢失数据。我们会在这个教程后面部分更深入讲解这部分功能,但是现在,我们只需要记住下面这3步来修改模型:

使用API

现在,让我们在交互式的Python shell里面使用一下Django提供给我们的免费API。要调用Python shell,使用下面的命令:
$python manage.py shell
我们使用这个命令来替代简单的python,因为manage.py设置了DJANGO_SETTINGS_MODULE环境变量,这个变量保存的是你的mysite/settings.py文件在Python中的导入路径。

当你在shel里的时候,我们来尝试一下数据库API:

>>> from polls.models import Question, Choice   #插入我们刚才写的类名

# 系统里现在还没有Question
>>> Question.objects.all()
<QuerySet []>

# 创建一个新的Question.
# 在配置文件里对时区的支持默认是开启的,所以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=<UTC>)

# 通过修改属性来修改字段值,然后调用save()方法保存到数据库
>>> q.question_text = "What's up?"
>>> q.save()

# objects.all() 显示数据库中的所有question
>>> Question.objects.all()
<QuerySet [<Question: Question object (1)>]>

稍等一下,<Question: Question object (1)>并不是这个对象一个有效的表示。让我们来编辑Question模型(在polls/models.py)来修复这个问题。添加一个str()方法到Question和Choice里面。

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自动生成的管理站点里面 。
要注意有很多普通的Python方法, 我们添加一个常用的方法用来展示:

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 shell窗口:

>>> from polls.models import Question, Choice

# Make sure our __str__() addition worked.
>>> 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?>]>

# 取出今年发布的question
>>> 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

# 给Question生成很多Choices. INSERT声明中的create调用,会创建一个新的Choice
# 对象,将这个choice添加到可用的choices集合中,并且返回这个新的Choice对象
# Django创建一个集合来保存外键关联的“另外一边” (例如question的choice),也
# 可以通过API来访问
>>> q = Question.objects.get(pk=1)

# 显示关联的对象集合中的任何choices——目前为止还一个都没有
>>> q.choice_set.all()
<QuerySet []>

# 创建3个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对象有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 会自动跟踪你需要的关系,使用双下划线来分开关联。这个可以查到你想要
# 的任何深度,没有任何限制。
# 找出pub_date在今年的任何Choices (重新使用我们上面创建的 'current_year' 变量).
>>> Choice.objects.filter(question__pub_date__year=current_year)
<QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]>

# 删除其中一个choices. 使用delete()方法。
>>> c = q.choice_set.filter(choice_text__startswith='Just hacking')
>>> c.delete()

数据库API的更多知识,建议查询Django的官方文档

介绍Django Admin

创建一个管理员用户

首先我们需要创建一个可以登录管理站点的用户,运行下面的命令:
$ python manage.py createsuperuser
输入你想到的用户名然后按enter:
Username: admin
然后会要求你输入email地址:
Email address: admin@example.com
最后一步会要求你输入admin的密码,会要求你输入两次。

Password: **********
Password (again): *********
Superuser created successfully.
启动开发服务器

Django的管理站点默认已经启用了,我们启动开发服务器来看一下,打开浏览器,输入管理站点的地址:http://localhost:8000/admin,或者将localhost替换成你的虚拟机IP地址,会看到下面的管理页面:

image.png
如果你在mysite/settings.py文件里将translation设置打开了,并且你自己的浏览器设置为你自己的语言,那么显示的管理站点页面可能就是你本地语言。
进入管理站点

输入刚才第一步配置好的账号密码,然后点击登录,就能进入到管理站点,会看到下面的内容:


image.png

可以看到几个能够编辑的内容,例如groups和users,这些是django.contrib.auth提供的,就是前面说的由Django提供的认证模块。

让投票应用可以在admin里面修改

但是我们的投票应用在哪呢,并没有在admin的主页上面显示出来。这是因为我们还需要再做一件事,告诉admin,Question由一个admin接口。要做这个,我们打开polls/admin.py文件。然后编辑它,内容如下所示:

from django.contrib import admin

from .models import Question

admin.site.register(Question)
探索免费的管理功能

上面我们将Question类注册到了admin站点,Django就知道怎么在站点上显示这个内容,如下所示:

image.png
点击Questions,然后可以看到Question的修改列表。这个页面显示出数据库中所有的Question实例,让你可以选择其中的Question,并对它们进行操作。我们可以看到刚才创建的"What's new" Question:
image.png
点击进去就可以看到这个问题的编辑页面:
image.png
这里要注意的一些事情:

页面的顶部给了很多选项:

如果你在第一jie创建question的时候Date published的值和时间不匹配,意味着你可能忘了正确设置TIME_ZONE的值。修改它,然后重新加载页面,正确的值就会出现。

通过点击Today和Now图标来修改发布日期,然后点“Save and continue editing”,再点击右上方的“History”,就会看到这个对象的编辑历史。可以看到编辑的用户和编辑的时间:


image.png

上面就是设置数据库、创建数据库模型、使用数据库对象、使用管理站点的所有内容。如果你对上面的这些内容熟悉以后,就可以查看第三部分,为应用添加更多的视图。

上一篇下一篇

猜你喜欢

热点阅读