Django从入门到实战:编写你的第一个 Django 应用(2)

30 篇文章 8 订阅
9 篇文章 4 订阅

上篇博客我们学会了创建项目,应用,基本的请求和响应流程,

一周之际在于周一,继续走起。

现在我们将建立数据库,创建第一个模型,并主要关注 Django 提供的自动生成的管理页面。

数据库配置

现在,打开 mywebsite/settings.py 。这是个包含了 Django 项目设置的 Python 模块。

通常,这个配置文件使用 SQLite 作为默认数据库。如果你不熟悉数据库,或者只是想尝试下 Django,这是最简单的选择。Python 内置 SQLite,所以你无需安装额外东西来使用它。

当你开始一个真正的项目时,你可能更倾向使用一个更具扩展性的数据库,例如 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,则必须添加一些额外设置,比如USER,PASSWOED,HOST等等。而且请确认在使用前已经创建了数据库。你可以通过在你的数据库交互式命令行中使用 "CREATE DATABASE database_name;" 命令来完成这件事。

另外,还要确保该数据库用户中提供 mysite/settings.py 具有 "create database" 权限。这使得自动创建的test database能被以后使用。

如果你使用 SQLite,那么你不需要在使用前做任何事——数据库会在需要的时候自动创建。

编辑 mysite/settings.py 文件前,先设置TIME_ZONE 为你自己时区。

关于TIME_ZONE?

默认: 'America/Chicago'

由于Django首次发布时设置为 'America/Chicago',所以全局设置(如果在项目中没有定义,则使用settings.py)仍然是'America/Chicago'为了向后兼容。新项目模板默认为'UTC'

请注意,这不一定是服务器的时区。例如,一台服务器可以为多个Django供电的站点提供服务,每个站点都有一个单独的时区设置。

如果USE_TZFalse,这是Django存储所有日期时间的时区。如果USE_TZTrue,这是Django会使用显示模板日期时间,并解释在表单中输入的日期时间的默认时区。

如果您在Windows上运行Django,则TIME_ZONE必须将其设置为与系统时区匹配。

此外,关注一下文件头部的  INSTALLED_APPS 设置项。这里包括了会在你项目中启用的所有 Django 应用。应用能在多个项目中使用,你也可以打包并且发布应用,让别人使用它们。

通常,  INSTALLED_APPS 默认包括了以下 Django 的自带应用:

  • django.contrib.admain -- 管理员站点, 你很快就会使用它。
  • django.contrib.auth -- 认证授权系统。
  • django.contrib.contenttypes-- 内容类型框架。
  • django.contrib.sessions -- 会话框架。
  • django.contrib.mmessages-- 消息框架。
  • django.contrib.staticfiles-- 管理静态文件的框架。

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

默认开启的某些应用需要至少一个数据表,所以,在使用他们之前需要在数据库中创建一些表。请执行以下命令:

$ python manage.py migrate

这个migrate 命令检查 INSTALLED_APPS设置,为其中的每个应用创建需要的数据表,至于具体会创建什么,这取决于你的 mywebsite/settings.py 设置文件和每个应用的数据库迁移文件(我们稍后会介绍这个)。这个命令所执行的每个迁移操作都会在终端中显示出来。

如果你感兴趣的话,运行你数据库的命令行工具,并输入 

(PostgreSQL)\dt

(MySQL)SHOW TABLES;

(SQLite) .schema 

(Oracle) SELECT TABLE_NAME FROM USER_TABLES;

可以查看 Django 到底创建了哪些表。

就像之前说的,为了方便大多数项目,我们默认激活了一些应用,但并不是每个人都需要它们。如果你不需要某个或某些应用,你可以在运行 migrate前毫无顾虑地从INSTALLED_APPS 里注释或者删除掉它们。migrate命令只会为在 INSTALLED_APPS 里声明了的应用进行数据库迁移。

 

创建模型

在 Django 里写一个数据库驱动的 Web 应用的第一步是定义模型 - 也就是数据库结构设计和附加的其它元数据。

模型是真实数据的简单明确的描述。它包含了储存的数据所必要的字段和行为。

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

在这个简单的投票应用中,需要创建两个模型:问题 Question 和选项 ChoiceQuestion 模型包括问题描述和发布时间。Choice 模型有两个字段,选项描述和当前得票数。每个选项属于一个问题。

 

这些概念可以通过一个简单的 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 类的实例 ,每个 Field 类实例变量的名字(例如 question_text 或 pub_date )也就是数据库字段名。
  • 在上面的例子中,我们只为 Question.pub_date 定义了对人类友好的名字。对于模型内的其它字段,它们的机器友好名也会被作为人类友好名使用。
  • 定义某些 Field 类实例需要参数。例如 CharField需要一个 max_length 参数。这个参数的用处不止于用来定义数据库结构,也用于验证数据。
  • Field 也能够接收多个可选参数;
  • 在上面的例子中:我们将 votes 的 default 也就是默认值,设为0。
  • 注意:我们使用 ForeignerKey 定义了一个关系。这将告诉 Django,每个 Choice 对象都关联到一个Question 对象。Django 支持所有常用的数据库关系:多对一、多对多和一对一

激活模型

上面的一小段用于创建模型的代码给了 Django 很多信息,通过这些信息,Django 可以:

  • 为这个应用创建数据库 schema(生成 CREATE TABLE 语句)。
  • 创建可以与 Question 和 Choice 对象进行交互的 Python 数据库 API。

但是首先得把 polls 应用安装到我们的项目里。

注意:

Django 应用是“可插拔”的。你可以在多个项目中使用同一个应用。除此之外,你还可以发布自己的应用,因为它们并不会被绑定到当前安装的 Django 上。

为了在我们的工程中包含这个应用,我们需要在配置类 INSTALLED_APPS 中添加设置。因为 PollsConfig 类写在文件 polls/apps.py 中,所以它的点式路径是 'polls.apps.PollsConfig'。在文件 mywebsite/settings.py中 INSTALLED_APPS子项添加点式路径后,它看起来像这样:

#mywebsite/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 会检测你对模型文件的修改(在这种情况下,你已经取得了新的),并且把修改的部分储存为一次 迁移

迁移是 Django 对于模型定义(也就是你的数据库结构)的变化的储存形式 - 没那么玄乎,它们其实也只是一些你磁盘上的文件。如果你想的话,你可以阅读一下你模型的迁移数据,它被储存在 polls/migrations/0001_initial.py 里。别担心,你不需要每次都阅读迁移文件,但是它们被设计成人类可读的形式,这是为了便于你手动修改它们。

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)和模型名的小写形式( question 和 choice)连接而来。(如果需要,你可以自定义此行为。)
  • 主键(IDs)会被自动创建。(当然,你也可以自定义。)
  • 默认的,Django 会在外键字段名后追加字符串 "_id" 。(同样,这也可以自定义。)
  • 外键关系由 FOREIGN KEY 生成。你不用关心 DEFERRABLE 部分,它只是告诉 PostgreSQL,请在事务全都执行完之后再创建外键关系。
  • 生成的 SQL 语句是为你所用的数据库定制的,所以那些和数据库有关的字段类型,比如 auto_increment(MySQL)、 serial (PostgreSQL)和 integer primary key autoincrement (SQLite),Django 会帮你自动处理。那些和引号相关的事情 - 例如,是使用单引号还是双引号 - 也一样会被自动处理。
  • 这个 sqlmigrate 命令并没有真正在你的数据库中的执行迁移 - 它只是把命令输出到屏幕上,让你看看 Django 认为需要执行哪些 SQL 语句。这在你想看看 Django 到底准备做什么,或者当你是数据库管理员,需要写脚本来批量处理数据库时会很有用。

如果你感兴趣,你也可以试试运行 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来跟踪执行过哪些迁移)并应用在数据库上 - 也就是将你对模型的更改同步到数据库结构上。

迁移是非常强大的功能,它能让你在开发过程中持续的改变数据库结构而不需要重新删除和创建表 - 它专注于使数据库平滑升级而不会丢失数据。

现在,你只需要记住,改变模型需要这三步:

数据库迁移被分解成生成应用两个命令是为了让你能够在代码控制系统上提交迁移数据并使其能在多个应用里使用;这不仅仅会让开发更加简单,也给别的开发者和生产环境中的使用带来方便。

 

初试 API

现在让我们进入交互式 Python 命令行,尝试一下 Django 为你创建的各种 API。通过以下命令打开 Python 命令行:

$ python manage.py shell

我们使用这个命令而不是简单的使用 "Python" 是因为 manage.py 会设置 DJANGO_SETTINGS_MODULE 环境变量,这个变量会让 Django 根据 mywebsite/settings.py 文件来设置 Python 包的导入路径。

当你成功进入命令行后,来试试 数据库 API 吧:

>>> from polls.models import Choice, Question   #导入我们刚写的模型类


#系统中还没有任何问题
>>> Question.objects.all()
<QuerySet []>


# 创建一个新问题。默认设置文件中启用了对时区的支持,因此Django期望使用tzinfo为pub_date设置日期时间。使用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.objects.all()
<QuerySet [<Question: Question object (1)>]>

然而,<Question: Question object (1)> 对于我们了解这个对象的细节没什么帮助。让我们通过编辑 Question 模型的代码(位于 polls/models.py 中)来修复这个问题。给 Question 和 Choice 增加 __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 datetime和 from django.utils import timezone分别导入了Python的标准 模块和Django中和时区相关的工具模块。

保存文件然后通过 命令再次打开Python交互式命令行:python manage.py shell

>>> from polls.models import Choice, Question

# 确保我们的__str __()添加有效。
>>> 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?>]>


#获取今年发布的问题。
>>> 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


#给出问题几个选择。 create调用构造一个新的Choice对象,执行INSERT语句,将选择添加到可用选项集并返回新的Choice对象.Django创建一个集合来保存ForeignKey关系的“另一面”(例如问题的选择) )可以通过API访问。
>>> q = Question.objects.get(pk=1)


#显示相关对象集中的任何选项 - 目前为止都没有。
>>> q.choice_set.all()
<QuerySet []>


# 创建3个选项
>>> 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对象具有对其相关Question对象的API访问权限。
>>> c.question
<Question: What's up?>


#反之亦然:问题对象可以访问Choice对象。
>>> q.choice_set.all()
<QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]>
>>> q.choice_set.count()
3


#API会根据您的需要自动跟踪关系。
#使用双下划线分隔关系。
#这可以根据你的需要进行多级操作;没有限制。
#查找pub_date在今年的任何问题的所有选择
#(重用我们上面创建的'current_year'变量)。
>>> Choice.objects.filter(question__pub_date__year=current_year)
<QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]>


#让我们删除其中一个选项。使用delete()。
>>> c = q.choice_set.filter(choice_text__startswith='Just hacking')
>>> c.delete()

 

介绍 Django 管理页面

Django 全自动地根据模型创建后台界面。Django 产生于一个公众页面和内容发布者页面完全分离的新闻类站点的开发过程中。站点管理人员使用管理系统来添加新闻、事件和体育时讯等,这些添加的内容被显示在公众页面上。Django 通过为站点管理人员创建统一的内容编辑界面解决了这个问题。管理界面不是为了网站的访问者,而是为管理者准备的。

创建一个管理员账号:

首先,我们得创建一个能登录管理页面的用户。请运行下面的命令:

$ python manage.py createsuperuser

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

Username: admin

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

Email address: admin@example.com

最后一步是输入密码(不可见)。你会被要求输入两次密码,第二次的目的是为了确认第一次输入的确实是你想要的密码。

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

修改用户的密码,请使用以下命令:python manage.py changepassword username

启动开发服务器

Django 的管理界面默认就是启用的。让我们启动开发服务器,看看它到底是什么样的。

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

$ python manage.py runserver

现在,打开浏览器,转到你本地域名的"/admin/"目录, --比如" http://127.0.0.1:800/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

 

点击"Questions" 。现在看到是问题"Questions" 对象的列表"change list" 。这个界面会显示所有数据库里的问题Question 对象,你可以选择一个来修改。这里现在有我们在上一部分中创建的“What's up?” 问题。

Polls change list page

点击“What's up?” 来编辑这个问题(Question)对象:

Editing form for question object

注意事项:

  • 这个表单是从问题Question模型中自动生成的
  • 不同的字段类型(日期时间字段DateTimeField 、字符字段CharField)会生成对应的HTML输入控件。每个类型的字段都知道它们该如何在管理页面里显示自己。
  • 每个日期时间字段 DateTimeField 都有JavaScript写的快捷按钮。日期有转到今天(Today)的快捷按钮和一个弹出式日历界面。时间有设为现在(Now)的快捷按钮和一个列出常用时间的方便的弹出式列表。

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

  • 保存(Save) - 保存改变,然后返回对象列表。
  • 保存并继续编辑(Save and continue editing) - 保存改变,然后重新载入当前对象的修改界面。
  • 保存并新增(Save and add another) - 保存改变,然后添加一个新的空对象并载入修改界面。
  • 删除(Delete) - 显示一个确认删除页面。

如果显示的“发布日期(Date Published)”和你在上篇博客里创建它们的时间不一致,这意味着你可能没有正确的设置TIME_ZONE。改变设置,然后重新载入页面看看是否显示了正确的值。

通过点击“今天(Today)” 和“现在(Now)” 按钮改变“发布日期(Date Published)”。然后点击“保存并继续编辑(Save and add another)”按钮。然后点击右上角的“历史(History)”按钮。你会看到一个列出了所有通过Django 管理页面对当前对象进行的改变的页面,其中列出了时间戳和进行修改操作的用户名:

History page for question object

熟悉了数据库API之后,下一部分我们将会学习如何为投票应用添加更多视图。各位, 下篇博客见了。

 

写在最后,欢迎关注一个跨行学python人的微信公众号:大众学python

掏出手机扫一扫:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

薛定谔的猫96

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值