Python学习之路19-设置应用程序的样式并对其进行部署

《Python编程:从入门到实践》笔记。
本篇将对Django项目做最后的完善。本篇也是这本书的最后一篇。

1. 前言

在本篇中,我们将:

  • 使用Bootstrap库设置样式;
  • 把项目部署到Heroku上。

2. 设置项目“学习笔记”的样式

之前关注的都是项目的功能,现在来为项目添加样式。

我们将使用django-bootstrap3来设置样式。首先请在虚拟环境中安装这个第三方库。

然后像之前在项目settings.py中注册我们自己编写的APP一样,注册bootstrap3这个应用程序。

还需要包含django-bootstrap3包含jQuery,在settings.py末尾添加如下代码:

1
2
3
4
5
6
7
-- snip --
LOGIN_URL = '/users/login/'

# django-bootstrap3的设置
BOOTSTRAP3 = {
"include_jquery": True,
}

2.1 修改base.html

2.1.1 定义HTML头部

实现访问项目的每个页面时,浏览器标题都现实这个网站的名称。另外还添加了一些在模板中使用Bootstrap所需的信息。删除base.html的全部代码,并添加如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{% load bootstrap3 %}

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="x-ua-compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">

<title>Learning Log</title>

{% bootstrap_css %}
{% bootstrap_javascript %}
</head>
</html>

第12行使用了django-bootstrap3的一个自定义模板标签,它让Django包含所有的Bootstrap样式文件。第13行启用可能在页面中使用的所有交互式行为,如可折叠的导航栏。

2.1.2 定义导航栏

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
-- snip --
</head>
<body>
<!-- Static navbar -->
<nav class="navbar navbar-default navbar-static-top">
<div class="container">

<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse"
data-target="#navbar" aria-expanded="false" aria-controls="navbar">
</button>
<a class="navbar-brand" href="{% url 'learning_logs:index' %}">Learning Log</a>
</div>

<div id="navbar" class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li><a href="{% url 'learning_logs:topics' %}">Topics</a></li>
</ul>

<ul class="nav navbar-nav navbar-right">
{% if user.is_authenticated %}
<li><a>Hello, {{ user.username }}.</a></li>
<li><a href="{% url 'users:logout' %}">log out</a></li>
{% else %}
<li><a href="{% url 'users:register' %}">register</a></li>
<li><a href="{% url 'users:login' %}">log in</a></li>
{% endif %}
</ul>
</div><!--/.nav-collapse -->

</div>
</nav>
</body>
</html>

navbarnavbar-defaultnavbar-static-top是三个选择器,在nav块中的内容将根据选择器在Bootstrap中定义的样式规则来设置样式(额,html中选择器的概念有点忘了,不过不要紧,我们的任务并不是研究HTML)。在第20-28行中是我们之前编写的判断语句,只不过被放在了ul块中,并且设置了一个选择器navbar-right

2.1.3 定义页面的主要部分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
-- snip --
</head>
<body>
-- snip --
<div class="container">

<div class="page-header">
{% block header %}{% endblock %}
</div>
<div>
{% block content %}{% endblock %}
</div>

</div> <!-- /container -->
</body>
</html>

这部分包含一个div块,该块的class属性是container(容器),容器中包含两个元素:一个新增的header块和之前用到的content块。header块的内容告诉用户页面包含哪些信息以及用户可以在页面上执行哪些操作,其class属性值page-header将一系列样式应用于这个块。base.html的修改到此为止。

2.2 使用jumbotron设置主页样式

下面使用新定义的header块及另一个名为jumbotron的Bootstrap元素修改主页。jumbotron元素是一个大框,通常用于在主页中呈现项目的简要描述,修改index.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{% extends "learning_logs/base.html" %}

{% block header %}
<div class="jumbotron">
<h1>Track your learning.</h1>
</div>
{% endblock header %}

{% block content %}
<h2>
<a href="{% url 'users:register' %}">Register an account</a>to make your own
Learning Log, and list the topics you're learning about.
</h2>
<h2>
Whenever you learn something new about a topic, make an entry summarizing
what you've learned.
</h2>
{% endblock content %}

header块中,我们用一个jumbotron元素来修饰一条简短的标语,让首次访问者大致知道网站功能。随后再content块中描述了两种主要操作。下图是实际效果:

2.3 设置登录页面样式

现在的代码改进了登录页面的整体外观(因为修改了base.html),现在来改进登录表单,修改login.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{% extends "learning_logs/base.html" %}
{% load bootstrap3 %}

{% block header %}
<h2>Log in to your account.</h2>
{% endblock header %}

{% block content %}

<form class="form" method="post" action="{% url 'users:login' %}">
{% csrf_token %}
{% bootstrap_form form %}

{% buttons %}
<button class="btn btn-primary" name="submit">log in</button>
{% endbuttons %}
<input type="hidden" name="next" value="{% url 'learning_logs:index' %}"/>
</form>

{% endblock content %}

第2行代码加载了bootstrap3模板标签;

header块描述这个页面时做什么的;

删除了之前的if form.errors代码块,因为django-bootstrap3位自动管理表单错误;

form块中添加了属性“form",然后使用标签模板bootstrap_form来显示表单,这个标签替换掉了之前的form.as_p

button也使用Bootstrap样式进行了替换,下面是实际效果图:

2.4 设置new_topic.html页面样式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{% extends "learning_logs/base.html" %}
{% load bootstrap3 %}

{% block header %}
<h2>Add a new topic:</h2>
{% endblock header %}

{% block content %}

<form class="form" action="{% url 'learning_logs:new_topic' %}" method="post">
{% csrf_token %}
{% bootstrap_form %}

{% buttons %}
<button class="btn btn-primary" name="submit">add topic</button>
{% endbuttons %}
</form>

{% endblock content %}

上面的修改大多都类似于对login.html的修改。

2.5 设置topics.html页面样式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{% extends "learning_logs/base.html" %}

{% block header %}
<h1>Topics</h1>
{% endblock header %}

{% block content %}

<ul>
{% for topic in topics %}
<li>
<h3>
<a href="{% url 'learning_logs:topic' topic.id %}">{{ topic }}</a>
</h3>
</li>
{% empty %}
<li>No topics have been added yet.</li>
{% endfor %}
</ul>
<h3><a href="{% url 'learning_logs:new_topic' %}">Add a new topic:</a></h3>

{% endblock content %}

这里并没有加载bootstrap3,因为该文件中并没有使用任何bootstrap3自定义标签。

2.6 设置topic.html中条目的样式

topic页面包含的内容比其他大部分页面都多,所以样式设置要多一些,我们将使用Bootstrap面板(panel)来突出每个条目。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
{% extends "learning_logs/base.html" %}

{% block header %}
<h2>{{ topic }}</h2>
{% endblock header %}

{% block content %}
<p>
<a href="{% url 'learning_logs:new_entry' topic.id %}">add new entry</a>
</p>

{% for entry in entries %}
<div class="panel panel-default">
<div class="panel-heading">
<h3>
{{ entry.date_added|date:"M d, Y H:i" }}
<small>
<a href="{% url 'learning_logs:edit_entry' entry.id %}">edit
entry</a>
</small>
</h3>
</div>
<div class="panel-body">
{{ entry.text|linebreaks }}
</div>
</div> <!-- panel -->
{% empty %}
<li>
There are no entries for this topic yet.
</li>
{% endfor %}

{% endblock content %}

只修改了样式,并没有修改Django代码。下图是实际效果:

至此已完成了对页面的修改。

3. 部署“学习笔记”

由于对Web应用不是很了解,笔者查阅资料现在大多用Apache和Nginx来部署Web项目,但本书使用Heroku来部署我们的Web项目。

请到Heroku的官网注册账号,它提供免费使用服务,并到https://toolbelt.heroku.com/ 下载命令行工具。

同时,还需要在Django项目所在的虚拟环境中安装一些额外的包:

dj-database-url:帮助Django与Heroku使用的数据库进行通信;

dj-staticstatic3:帮助Django正确管理静态文件(静态文件包括样式规则和JavaScript文件);

gunicorn:一个服务器软件,能够在在线环境中支持应用程序提供的服务。

3.1 创建包含包列表的文件requirements.txt

Heroku需要知道我们的项目依赖于哪些包,使用pip命令来生成这个文件:

1
(venv)learning_log>pip freeze > requirements.txt

下面是这个文件所包含的内容(“如果是Windows系统,看到的内容可能不全”——这是书中提示,然而这里的内容还比书中多了一个pytz):

1
2
3
4
5
6
7
dj-database-url==0.5.0
dj-static==0.0.6
Django==2.0.4
django-bootstrap3==9.1.0
gunicorn==19.7.1
pytz==2018.4
static3==0.7.0

在部署项目时,Heroku将创建一个环境,并根据这个文件安装其中的所有包。也因此,项目部署到Heroku后,行为将与它在本地系统上一样。

还需要在包列表中添加psycopg2,它帮助Heroku管理活动数据库。需在requirements.txt最后一行添加如下代码:psycopg2>=2.6.1。该语句表示,有新版则装最新版,没有的话最低安装2.6.1版本。

3.2 确定Python版本

如果没有指定Python版本,Heroku将使用其当前的Python默认版本。下面来确保Heroku使用我们所使用的版本。如果不知道使用的python的版本,请在项目所在虚拟环境中执行python --version

1
2
(venv)learning_log>python --version
Python 3.6.4

再在manage.py所在的文件夹中新建一个名为runtime.txt的文件,输入Python的版本:

1
python-3.6.4

注意:单词小写,中间有一个连字符!

3.3 为部署到Heroku而修改settings.py

settings.py末尾添加一个片段,指定一些Heroku环境设置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
-- snip --
# 书中的判断语句是 if os.getcwd() == '/app':
# 现在估计是Heroku升级了,改为了下面的语句,否则待会儿部署的时候会出错
if os.environ['HOME'] == "/app":
import dj_database_url

DATABASES = {
"default": dj_database_url.config(default="postgres://localhost")
}

# 让request.is_secure()承认X-Forwarded-Proto头
SECURE_PROXY_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")

# 支持所有的主机头(host header)
ALLOWED_HOSTS = ["*"]

# 静态资产配置
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
# 书中设置是这样的: STATIC_ROOT = "staticfiles"
STATIC_ROOT = os.path.join(BASE_DIR, 'static')
STATICFILES_DIRS = (
os.path.join(BASE_DIR, "static"),
)

第2行中,使用getcwd()函数获取当前的工作目录,在Heroku中,这个目录总/app。在本地部署中,这个目录通常是项目文件的名称。这个if测试确保仅当项目被部署到Heroku时才运行这个代码块。这种结构让我们能够将统一配置文件用于本地开发环境和在线服务器。

在第3行,导入了dj_database_url,用于在Heroku上配置服务器。Heroku使用PostgreSQL(也叫Postgres,一种比SQLite更高级的数据库),这些配置使得项目在Heroku上使用该数据库。

其他配置作用分别如下:支持HTTPS请求(第10行);让Django能够使用Heroku的URL来提供项目支持的服务(第13行);设置项目,使其能够在Heroku上正确地提供静态文件(第16-20行)。

3.4 创建启动进程的Procfile

Procfile告诉Heroku启动那些进程,以便能够正确地提供项目支持的服务。这个文件只包含一行,文件名为Procfile,不带扩展名,保存到项目根目录。

1
web: gunicorn learning_log.wsgi --log-file -

这段代码让Heroku将gunicorn用过服务器,并使用learning_log/wsgi.py中的设置来启动应用程序。标志log-file告诉Heroku应将哪些类型的时间写入日志。

3.5 为部署到Heroku而修改wsgi.py

因为Heroku需要的设置与目前一直使用的设置稍有不同,所以还需要修改wsgi.py文件:

1
2
3
4
5
6
7
8
9
import os

from django.core.wsgi import get_wsgi_application
from dj_static import Cling

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "learning_log.settings")

# 原代码是 application = get_wsgi_application()
application = Cling(get_wsgi_application())

注意,这里只有第4,9行是添加的,其余都是自带的。

3.6 创建用于存储静态文件的目录

在Heroku上,Django搜集所有的静态文件,并将它们放在一个地方,以便高效管理。我们需要手动创建这样一个文件夹。在项目文件夹learning_log中,也有一个名为learning_log的子文件夹,在这个子文件夹中,新建一个名为static的文件夹,即这个文件夹的路径为:learning_log/learning_log/static/。由于项目被推送到Heroku时,它将不包含空文件夹,所以,在static文件夹中还需要创建一个占位文件placeholder.txt

1
2
This file ensures that learning_log/static/ will be added to the project.
Django will collect static files and place them in learning_log/static/.

3.7 在本地使用gunicorn服务器

如果你使用的是Linux或OS X,可在部署到Heroku前尝试在本地使用gunicorn服务器。为此,在虚拟环境中执行命令:

1
2
3
4
5
6
(ll_env)learning_log$ heroku local
Installing Heroku Toolbelt v4... done
-- snip --
forego | starting web.1 on port 5000
web.1 | [2018-04-27 14:00:00 -0800] [12875] [INFO] Starting gunicorn 19.3.0
-- snip --

但如果使用的是Windows,请跳过这个步骤(笔者用的是Windows,所以上面的输出是从书上照搬过来的)。

3.8 使用Git跟踪项目文件

Heroku Toolbelt包含Git,这里不再介绍Git怎么安装。

Git跟踪谁修改了项目,即便项目由一个人开发。为了实现跟踪,需要提供用户名和email,但对于联系项目,这俩都可以随便起:

1
2
(ll_env)learning_log$ git config --global user.name "example"
(ll_env)learning_log$ git config --global user.email "test@example.com"

我们无需让Git跟踪项目中的每个文件,因此将让Git忽略一些文件。在项目根目录下创建一个名为 .gitignore 的文件,注意前面有一个实心句点,不含扩展名。在文件中输入:

1
2
3
ll_env/
__pycache__/
*.sqlite3

忽略ll_env目录是因为随时都可以重新创建它;忽略__pycache__是因为这个目录包含了Django运行.py文件时自动创建的.pyc文件,目前都是本地数据(如果使用的是python2.7,请将__pycache__替换为*.pyc);没有跟踪本地数据库是因为如果你在服务器上使用的是SQLite,当你将项目推送到服务器时,可能会不小心把服务器上的数据给覆盖掉。

最后,我们提交项目,而在提交之前需要为我们的项目初始化一个Git仓库,将所有必要的文件都加入到这个仓库中,并提交项目的初始状态,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
(ll_env) learning_log>git init
Initialized empty Git repository in E:/Code/Python/learning_log/.git/

(ll_env) learning_log>git add .

(ll_env) learning_log>git commit -am "Ready for deployment to heroku."
[master (root-commit) 4109cbc] Ready for deployment to heroku.
39 files changed, 745 insertions(+)
create mode 100644 .gitignore
-- snip --
create mode 100644 users/views.py

(ll_env) learning_log>git status
On branch master
nothing to commit, working tree clean

第一个命令在“学习笔记”所在的目录中初始化一个空仓库;第二个命令(最后有个句点!)将未被忽略的文件都添加到这个仓库中;第三个命令中的标志 -a 让Git在这个提交中包含所有修改过的文件,而标志 -m 让Git记录一条日志消息;第四个命令的输出表明当前位于分支master中,而工作目录是干净(clean)的,每当要将项目推送到Heroku时,都希望看到这个状态。

3.9 推送到Heroku

现在开始推送项目。在项目的虚拟环境中执行下面的命令:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
(ll_env) learning_log>heroku login
Enter your Heroku credentials:
Email: kevinwen701@gmail.com
Password: ************
Logged in as kevinwen701@gmail.com

(ll_env) learning_log>heroku create
Creating app... done, ⬢ pure-anchorage-27981
https://pure-anchorage-27981.herokuapp.com/ | https://git.heroku.com/pure-anchorage-27981.git

(ll_env) learning_log>git push heroku master
Counting objects: 46, done.
-- snip --
remote: Verifying deploy... done.
To https://git.heroku.com/pure-anchorage-27981.git
* [new branch] master -> master

首先,在终端会话中,使用你在Heroku官网创建的账号登陆,然后让Heroku创建一个空项目。Heroku生成的项目名由两个单词和一个数字组成(可以修改)。然后我们让Git将项目分值master推送到Heroku刚才建立的仓库中;Heroku随后使用这些文件在其服务器上创建项目。最后输出信息还给出了访问这个项目的URL。

大概上述命令执行完后,项目便部署好了,但还未对其做全面的配置。为核实正确地启动了服务器进程,请执行命令heroku ps

1
2
3
4
5
6
7
(ll_env) learning_log>heroku ps
Free dyno hours quota remaining this month: 550h 0m (100%)
For more information on dyno sleeping and how to upgrade, see:
https://devcenter.heroku.com/articles/dyno-sleeping

=== web (Free): gunicorn learning_log.wsgi --log-file - (1)
web.1: up 2018/04/27 16:36:42 +0800 (~ 6m ago)

当执行了这条命令后,输出指出项目还可在多长时间内处于活动状态。当超过这个时间后,将显示标准的服务器错误页面,而稍后我们将设置这个错误页面。在倒数第二行,我们发现启动了Procfile指定的进程。

现在,我们可以使用命令heroku open在浏览器中打开这个APP了:

1
(ll_env) learning_log>heroku open

它将自动打开浏览器,显示项目的主页。

3.10 在Heroku上建立数据库

要对Heroku项目执行Django和Python命令,可使用命令heroku run

1
2
3
4
5
6
7
8
9
(ll_env) learning_log>heroku run python manage.py migrate
Running python manage.py migrate on ⬢ pure-anchorage-27981... up, run.9204 (Free)
-- snip --
Operations to perform:
Apply all migrations: admin, auth, contenttypes, learning_logs, sessions
Running migrations:
-- snip --
Applying learning_logs.0003_topic_owner... OK
Applying sessions.0001_initial... OK

当执行上述命令后,Heroku创建一个终端会话来执行命令migrate(第2行)。从第6行起,Django应用默认迁移以及我们在开发“学习笔记”期间生成的迁移。

现在可以像在本地系统上一样使用它。然而其中并没有任何数据,因为之前的测试数据并没有复制到服务器中,而且也不建议将测试数据复制到服务器中。

3.11 改进Heroku部署

3.11.1 在Heroku上创建超级用户

从上面的命令可以看出,我们可以使用heroku run来执行一次性命令,但可以这样执行命令:在连接到了Heroku服务器的情况下,使用命令heroku run bash来打开Bash终端会话。我们将使用Bash终端会话来创建超级用户,以便能够访问在线应用程序的管理网站:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
(ll_env) learning_log>heroku run bash
Running bash on ⬢ pure-anchorage-27981... up, run.8093 (Free)
~ $ ls
learning_log learning_logs manage.py Procfile requirements.txt runtime.txt users
~ $ python manage.py createsuperuser
/app/.heroku/python/lib/python3.6/site-packages/psycopg2/__init__.py:144: UserWarning:
The psycopg2 wheel package will be renamed from release 2.8; in order to keep installing
from binary please use "pip install psycopg2-binary" instead. For details see:
<http://initd.org/psycopg/docs/install.html#binary-install-from-pypi>.
""")
Username (leave blank to use 'u10229'): ll_admin
Email address:
Password:
Password (again):
Superuser created successfully.
~ $ exit
exit
(venv) learning_log>

进入Bash后,我们首先执行了ls命令,以查看服务器上有哪些文件和目录,发现和本地系统相同。然后我们执行了常见超级用户的命令,执行过程和之前创建超级用户一样。现在可以通过在APP的URL后面加/admin/来登陆管理网站了。

3.11.2 在Heroku上创建对用户友好的URL

由于在服务器上我们的项目名不是learning_log,而是其他自动生成的名字,为此,我们使用一个命令来重命名这个APP:

1
2
3
4
5
(ll_env) learning_log>heroku apps:rename kevins-learning-log
Renaming pure-anchorage-27981 to kevins-learning-log... done
https://kevins-learning-log.herokuapp.com/ | https://git.heroku.com/kevins-learning-log.git
Git remote heroku updated
! Don't forget to update git remotes for all other local checkouts of the app.

3.12 确保项目的安全

当前这个项目存在一个严重的安全问题:settings.py中包含设置DEBUG=True,它在发生错误时显示调试信息。开发项目时,Django的错误页面向你显示了重要的调试信息,如果将项目部署到服务器后依然保留这个设置,讲给攻击者提供大量可供利用的信息。我们还需要确保任何人都无法看到这些信息,也不能冒充项目托管网站来重定向请求。

下面修改settings.py文件,以让我们能够在本地看到错误信息,但部署到服务器后不显示任何错误信息:

1
2
3
4
5
6
7
8
-- snip --
if os.environ["HOME"] == "/app":
-- snip --
# 只允许Heroku托管这个项目
ALLOWED_HOSTS = ["kevins-learning-log.herokuapp.com"]

DEBUG = False
-- snip --

然后我们提交并推送修改。先将修改提交到Git仓库,在推送到Heroku:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
(venv) E:\Code\Python\learning_log>git commit -am "Set DEBUG=False for heroku."
[master 563b175] Set DEBUG=False for heroku.
1 file changed, 6 insertions(+), 4 deletions(-)

(venv) E:\Code\Python\learning_log>git status
On branch master
nothing to commit, working tree clean

(venv) E:\Code\Python\learning_log>git push heroku master
-- snip --
remote: -----> Python app detected
remote: -----> Installing requirements with pip
-- snip --
remote: -----> Launching...
remote: Released v7
remote: https://kevins-learning-log.herokuapp.com/ deployed to Heroku
remote:
remote: Verifying deploy... done.
To https://git.heroku.com/kevins-learning-log.git
9bc1c2d..563b175 master -> master

Heroku发现仓库发生了变化,因此重建了项目,确保所有的修改都已生效。它不会重建数据库,因此本次无需执行命令migrate

现在如果访问未定义的扩展将会看到一个标准的错误页面,它不包含任何关于项目的具体信息。

3.13 创建自定义错误页面

编写两个风格与我们项目相符的404和500错误页面模板。这些模板必须放在根模板目录中。为此,在文件夹leraning_log/learning_log中新建一个文件夹templates,再在这个文件夹中新建一个404.html文件,并输入如下内容:

1
2
3
4
5
{% extends "learning_logs/base.html" %}

{% block header %}
<h2>The item you requested is not available.(404)</h2>
{% endblock header %}

再创建一个名为500.html的文件:

1
2
3
4
5
{% extends "learning_logs/base.html" %}

{% block header %}
<h2>There has been an internal error. (500)</h2>
{% endblock header %}

这些新文件要求对settings.py做细微的修改:

1
2
3
4
5
6
7
8
9
10
-- snip --
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'learning_log/templates')],
'APP_DIRS': True,
-- snip --
},
]
-- snip --

在将修改推送到服务器之前,可以在本地查看错误页面时什么样的,不过得先在本地设置DEBUG=False

1
2
3
4
5
# settings.py文件
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = False

ALLOWED_HOSTS = ["localhost"]

现在本地访问不存在的页面时将得到我们自定义的错误页面。

最后,想之前一样,将修改推送到服务器,代码不再演示,注意为推送添加一条简短的日志信息。

现在,如果用户手工请求不存在的主题或条目将导致500错误。Django尝试渲染请求的页面,但没有足够的信息来完成这项任务,进而引发500错误。对于这种情况,将其视为404错误更合适,为此可使用Django快捷函数get_object_or_404()。这个函数尝试从数据库获取请求的对象,当对象不存在时,引发404错误。我们在views.py中导入这个函数,并用它替换函数get()

1
2
3
4
5
6
from django.shortcuts import render, get_object_or_404
@login_required
def topic(request, topic_id):
"""显示单个主题及其所有的条目"""
topic = get_object_or_404(Topic, id=topic_id)
-- snip --

再次提交并推送修改。

3.14 部署总结

从前面这些例子可看出,开发与部署的过程如下:

①修改项目。如果创建了新文件,使用命令 git add. (最后有个句点!)将它们加到Git仓库中。如果要迁移数据库,也需要执行该命令,因为每个迁移都生成了新的迁移文件。

②执行 git commit -am "commit message",将修改提交到仓库。

③执行 git push heroku master 将修改推送到服务器。

④如果本地迁移了数据库,也需要迁移在线数据库,可以使用一次性命令 heroku run python manage.py migrate ,也可以使用 heroku run bash打开一个远程终端会话,再执行迁移。

3.15 设置SECRET_KEY

Django根据settings.py中的SECRET_KEY来实现大量的安全协议。本项目中设置的SECRET_KEY对一个练习项目来说已经足够了,但是对于生产网站,请务必认真对待这个值。

3.16 将项目从Heroku删除

Heroku限制了你可免费托管的项目数,另外,我们也不希望自己的账户中塞满大量的联系项目。除了可以登录到Heroku,在应用程序的Settings中手动删除项目,也可以在命令行中执行如下命令删除项目:

1
(ll_env)learning_log$ heroku apps:destroy --app appname

4. 总结

现在大家可以访问这个网站 https://kevins-learning-log.herokuapp.com (笔者在免费期过期前不会删除这个网站)。本篇主要介绍了如何使用Bootstrap来设置网页的样式,并学习了如何将项目部署到Heroku的服务器上。至此,Python的Django入门已经完成,这本书也已经看完。暂时告一段落。

VPointer wechat
欢迎大家关注我的微信公众号"代码港"~~
您的慷慨将鼓励我继续创作~~