网站,现在可谓是司空见惯,就连本书作者也有个人网站(www.itdiffer.com),用以向读者提供所编写书籍的参考资料。那么,怎么制作网站?如果从理论上说,这是一个听起来、讲起来都比较复杂的事情。首先要全面了解有关计算机网络的有关知识(推荐阅读《计算机网络》,谢希仁编著,电子工业出版社出版),然后要能够运用多项技术,比如搭建服务器并部署有关程序、创建数据库、编写网站代码(还分前端和后端)、配置网站域名等,甚至还要考虑网站的并发量、安全性。所以,通常制作网站的项目,都是由若干个人组成一个团队协作完成。
不过,随着技术发展,制作网站的技术门槛正在不断降低,很多底层的东西都被“封装”和“模块化”,开发者将更多精力集中在网站的功能上。这就类似于 Python 中的包和模块一样,比如12.2.2 节中使用的 sqlite3
模块,我们不需要了解其内部工作机制,关注点在于用它实现数据库的连接和操作。
Python 生态中,提供了很多制作网站的包——更习惯的说法是Web框架(Web Framework),比如 Web.py 、Tornado 、Flask 、Django 等,不同框架会有各自的特点,很难用“好、坏”这种简单标准来评价——有的人热衷于“二进制”思维,但愿本书读者不要陷入“0/1”口水战。
下面使用 Django 框架,简要演示用它快速制作网站的流程。
Django 的官方网站(https://www.djangoproject.com/)上显示,撰写本节内容时所发布的最新版是 Django 3.2.5 ,此前 Django 有过 2.x 和 1.x 的各个版本。读者在阅读到本书并调试代码的时候,或者阅读其他有关资料时,务必注意版本问题,不同版本之间会有所差异。此外,围绕 Django 框架还有很多第三方插件(亦即 Python 语言的包或模块),它们也有所适用的版本。
其实,在第11章11.5节,已经在虚拟环境中安装了 Django ,下面就启动该虚拟环境,在其中用 Django 框架制作网站。
Django 中的项目(Project)可以看做是一个专有名词,后面还有一个与之有关的名词应用(Application)。所谓项目,可以理解为一个网站。
在虚拟目录 myvenv
内,创建一个 Django 项目(要确认已经进入虚拟环境,参阅第11章11.5节),执行如下指令(本节演示中所有操作系统指令,均为 Linux 指令,请使用 Windows 系统的读者注意):
(myvenv) myvenv % ls
bin include lib pyvenv.cfg
(myvenv) myvenv % django-admin startproject mysite
(myvenv) myvenv % ls
bin include lib mysite pyvenv.cfg
指令 django-admin startproject mysite
创建名为 mysite
的项目,执行此指令后,在当前目录中新增了 ./mysite
子目录。这个子目录并非是空的,Django 会默认为其添加如下内容(以下显示此时 ./mysite
的结构):
(myvenv) myvenv % tree mysite
mysite
├── manage.py
└── mysite
├── __init__.py
├── asgi.py
├── settings.py
├── urls.py
└── wsgi.py
1 directory, 6 files
然后进入到项目的子目录中,执行 manage.py
程序:
(myvenv) myvenv % cd mysite
(myvenv) mysite % python manage.py runserver
Watching for file changes with StatReloader
... (省略其他显示信息)
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
这样我们就把一个基于 Django 的网站跑起来了,由提示信息可知:
打开浏览器,在地址栏中输入 http://127.0.0.1:8000 或者 http://localhost:8000 ,就会看到图12-3-1所示界面。
图12-3-1 网站默认首页
就这样,已经创建了一个非常简单的网站——好像挺容易的呀。
项目已经创建好,网站也有了,接下来要实现网站的具体功能。在 Django 中,人们把这些具体的功能称为应用(Application)。
结束图12-3-1所示的服务(按“Ctrl-C”组合键),继续在虚拟目录中执行如下操作:
(myvenv) mysite % ls
db.sqlite3 manage.py mysite
与12.3.1节所看到的 ./mysite
目录结构相比较,这里多了一个文件 db.sqlite3
,这个文件就是12.2.2节所介绍过的 SQLite 数据库文件,Django 默认使用此类型的数据库,本节的浮光掠影地演示中,也使用这个数据库。
在当前目录中,继续执行:
(myvenv) mysite % python manage.py startapp book
(myvenv) mysite % ls
book db.sqlite3 manage.py mysite
指令 python manage.py startapp book
创建一个名为 book
的应用,并在当前目录中以子目录 ./book
的形式表现。这个子目录中也不是空的,默认配置如下:
(myvenv) mysite % tree book
book
├── __init__.py
├── admin.py
├── apps.py
├── migrations
│ └── __init__.py
├── models.py
├── tests.py
└── views.py
1 directory, 7 files
如果把应用理解为项目的子集,当应用创建好之后,就需要向项目“汇报”存在此应用了。用 IDE 工具打开项目目录中的 ./mysite/settings.py
文件(如12.3.1节中执行 tree mysite
后的显示的目录结构),找到第 33 行,对 INSTALLED_APPS
的值做如下修改:
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'book', # 增加一个应用
]
如此就完成了应用的配置——不要忘记保存文件。
一般的动态网站(与之对应的是“静态网站”)都有数据库。Django 为了简化开发,让开发者用 Python 语言可以实现一切,于是对数据库操作提供了 ORM 技术,即 Object-Relational Mapping(对象关系映射)。ORM 的作用是在关系型数据库和业务实体对象之间进行映射,这样在操作业务对象时,就不需要再去和复杂的 SQL 语句打交道,只需简单地操作对象的属性和方法。
Django 的 ORM 实现方式就是编写数据模型类,这些类可以写到任何文件中,通常写在每个应用的models.py
文件中。每个数据模型类都是 django.db.models.Model
的子类。应用的名称(小写字母)和数据模型类的名称(小写字母)共同组成一个数据库表的名称。
用 IDE 打开 ./book/models.py
文件,并编写如下数据模型类:
from django.db import models
from django.utils import timezone
from django.contrib.auth.models import User
class Articles(models.Model):
title = models.CharField(max_length=300) # (1)
body = models.TextField()
publish = models.DateTimeField(default=timezone.now)
class Meta: # (2)
ordering = ("-publish",)
def __str__(self):
return self.title
类 Articles
中的 title
、author
、body
、publish
是类属性,对应着数据库表的字段。每个类属性的类型(即字段类型)由等号右侧定义——可以理解为类属性的初始值为等号右侧的实例。例如注释(1)中,类属性(字段)title
引用了实例 models.CharField(max_length=300)
—— CharField()
类型,并且以参数 max_length=300
的形式说明字段的最大数量。
注释(2)从名称上看貌似 Python 中的元类,但它跟元类不同。在此处,通过 ordering = ("-publish",)
规定了 Articles
实例对象的显示顺序,负号表示按照 publish
字段值的倒序显示。
数据模型类 Articles
编写好之后,再执行如下操作,从而在数据库中创建对应的表结构:
# 第一步
(myvenv) mysite % python manage.py makemigrations
Migrations for 'book':
book/migrations/0001_initial.py
- Create model Articles
# 第二步
(myvenv) mysite % python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, book, contenttypes, sessions
Running migrations:
Applying contenttypes.0001_initial... OK
Applying auth.0001_initial... OK
Applying admin.0001_initial... OK
Applying admin.0002_logentry_remove_auto_add... OK
Applying admin.0003_logentry_add_action_flag_choices... OK
Applying contenttypes.0002_remove_content_type_name... OK
Applying auth.0002_alter_permission_name_max_length... OK
Applying auth.0003_alter_user_email_max_length... OK
Applying auth.0004_alter_user_username_opts... OK
Applying auth.0005_alter_user_last_login_null... OK
Applying auth.0006_require_contenttypes_0002... OK
Applying auth.0007_alter_validators_add_error_messages... OK
Applying auth.0008_alter_user_username_max_length... OK
Applying auth.0009_alter_user_last_name_max_length... OK
Applying auth.0010_alter_group_name_max_length... OK
Applying auth.0011_update_proxy_permissions... OK
Applying auth.0012_alter_user_first_name_max_length... OK
Applying book.0001_initial... OK
Applying sessions.0001_initial... OK
以上操作完成之后,已经在数据库 db.sqlite3
中创建了多个表,其中包括 Articles
类对应的表,图12-3-2显示的是当前已经有的表(其他表都是 Django 默认创建的。图示所用工具仍然是 12.2.2 节用过的软件)
图12-3-2 Article 对应的表结构
用 Django 默认的管理功能可以在已经创建的网站上发布文章。要使用此功能,必须先创建超级管理员。
(myvenv) mysite % python manage.py createsuperuser
Username (leave blank to use 'qiwsir'):
注意执行上述指令的位置,在项目目录中,即 manage.py
文件所在位置。输入用户名:
(myvenv) mysite % python manage.py createsuperuser
Username (leave blank to use 'qiwsir'): admin
Email address:
再输入邮箱(只要符合邮箱格式就行):
(myvenv) mysite % python manage.py createsuperuser
Username (leave blank to use 'qiwsir'): admin
Email address: admin@laoqi.com
Password:
输入密码。注意密码不能太简单,若太简单,系统会给予友好提示。输入一遍之后,再验证一遍,最终出现:
(myvenv) qiwsir@qiwsirs-MacBook-Pro mysite % python manage.py createsuperuser
Username (leave blank to use 'qiwsir'): admin
Email address: admin@laoqi.com
Password:
Password (again):
Superuser created successfully.
则超级管理员创建完毕——请牢记用户名和密码。
然后运行服务器。
(myvenv) qiwsir@qiwsirs-MacBook-Pro mysite % python manage.py runserver
Watching for file changes with StatReloader
...
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
在浏览器的地址栏中输入 http://127.0.0.1:8000/admin/ ,打开图12-3-3所示的界面。
图12-3-3 管理端登录界面
输入刚才创建的超级管理员的用户名和密码进入系统,如图12-3-4所示。
图12-3-4 管理界面
Groups 和 Users 是 Django 在用户管理应用中默认的两项。单击 Users 会看到当前项目仅有的一个用户 admin,当然可以增加用户,读者一定要试一试。
目前暂不研究用户问题,重点在于发布文章。但是目前还找不到发布文章的地方。
稍安勿躁。用 IDE 打开 ./book/admin.py
文件,输入如下代码,并保存文件。
from django.contrib import admin
from .models import Articles
admin.site.register(Articles)
在调试状态下,如果没有新增加的文件,只是修改了原有文件,则不需要重新启动 Django 服务(如果 Django 服务没有启动,请确保启动)。刷新图12-3-4所示的页面,即可看到图12-3-5所示的效果。
图12-3-5 增加对 book 应用的管理
点击“Add”按钮可以添加文章,如图12-3-6所示所示。
图12-3-6 编辑文章
然后点击“SAVE”按钮即可保存此文章。有兴趣可以多发布几篇。
发布文章的目的是给别人看,别人怎么看?接下来解决这个问题。
根据阅读网站上文章的经验,一般是有一个页面显示文章标题,然后点击标题,呈现该文章的完整内容。本节首先做一个显示标题列表的页面。
用 IDE 打开 ./book/views.py
文件,编写一个能够从数据库中已存储的文章标题的函数——在 Django 中称之为视图函数。
from django.shortcuts import render
from .models import Articles
def book_title(request):
posts = Articles.objects.all()
return render(request, "book/titles.html", {"posts":posts})
函数 book_title()
的参数是 request
,这个参数负责响应所接收到的请求且不能缺少,并总是位于第一的位置。除这个不可或缺的参数外,还可以根据需要在其后增加别的参数。
return 语句中的 render()
函数的作用是将数据渲染到指定模板上(关于模板,见下文内容)。render()
的第一个参数必须是 request
,然后是模板位置和所传送的数据,数据是用类字典的形式传送给模板的。
在 render()
中出现的 book/titles.html
就是标题列表的前端展示页面——被称为“模板”。在每一个应用中都可以有一个专门的模板目录。进入应用 book 的目录 ./book
,建立一个子目录 ./templates
,名称和位置必须如此,再按照如下方式建立有关文件和子目录。
(myvenv) book % tree templates
templates
└── book
└── titles.html
1 directory, 1 file
templates
目录是 Django 默认的存放本应用所需模板的目录,如果不用自定义的方式指定模板位置,Django 会在运行时自动来这里查找 render()
函数中所指定的模板文件。
然后编写 titles.html
文件的代码,这不是 Python 程序文件,是 HTML 文件,关于它的知识超出本书范畴,读者可以参考有关资料(在此处,可以照抄如下代码)。
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta http-equiv="X-UA-Compatible" content="IE=Edge">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>my website</title>
<!-- Bootstrap 核心 CSS 文件 -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css" integrity="sha384-HSMxcRTRxnN+Bdg0JdbxYKrThecOKuH5zCYotlSAcp1+c8xmyTe9GYg1l9a69psu" crossorigin="anonymous">
</head>
<body>
<div class="container">
<center><h1>老齐的书</h1></center>
<div class="row">
<div class="col-md-8">
<ul>
{% for post in posts %}
<li>{{ post.title }}</li>
{% endfor %}
</ul>
</div>
<div class="col-md-4">
<h2>老齐教室</h2>
<p>欢迎访问我的网站:www.itdiffer.com</p>
<p>包括各种学习资料</p>
</div>
</div>
</div>
<!-- Bootstrap 核心 JavaScript 文件 -->
<script src="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js" integrity="sha384-aJ21OjlMXNL5UyIl/XNwTMqvzeRMZH2w8c5cRVpzpU8Y5bApTppSuUkhZXN0VxHd" crossorigin="anonymous"></script>
</body>
</html>
视图函数和模板都编写好之后,要想通过网页访问,还需要进行 URL 配置。首先要配置 ./mysite/urls.py
,在这个文件中配置本项目的各个应用。
from django.contrib import admin
from django.urls import path
from django.conf.urls import url, include
urlpatterns = [
path('admin/', admin.site.urls),
path('book/', include("book.urls"), name='book'),
]
然后在 book
目录中创建文件 urls.py
,并写入如下代码:
from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^$', views.book_title, name="book_title"),
]
重启 Django 服务,在浏览器中输入 http://127.0.0.1:8000/book/ ,会看到图12-3-7所示界面,显示了文章标题。
图12-3-7 显示文章标题的界面
每一篇文章,在数据库中都会有唯一的 id,因此可以通过文章 id 从数据库中读出该文章,并显示与网页上。
按照用户的操作顺序,在 titles.html
页面中显示的标题应该有超链接,点击该超链接即向服务器请求显示该标题的文章,所以,应该在 titles.html
中显示标题的部分做如下修改。
<ul>
{% for post in posts %}
<li><a href="{{post.id}}">{{ post.title }}</a></li> \\ 修改
{% endfor %}
</ul>
而后编辑 ./book/views.py
,增加响应查看文章请求的视图函数 book_article()
。
def book_article(request, article_id):
article = Articles.objects.get(id=article_id)
return render(request, 'book/content.html', {"article": article})
视图函数 book_article
的参数除了 request
之外,还有 article_id
,这个参数需要通过前端的请求获得,在后续的 URL 配置中会给予实现。
然后编写展示文章内容的模板文件 ./templates/book/content.html
文件,代码如下:
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta http-equiv="X-UA-Compatible" content="IE=Edge">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>my website</title>
<!-- Bootstrap 核心 CSS 文件 -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css" integrity="sha384-HSMxcRTRxnN+Bdg0JdbxYKrThecOKuH5zCYotlSAcp1+c8xmyTe9GYg1l9a69psu" crossorigin="anonymous">
</head>
<body>
<div class="container">
<center><h1>老齐的书</h1></center>
<div class="row">
<div class="col-md-8">
<center><h2>{{article.title}}</h2></center>
<center>{{article.publish}}</center>
<p class="lead">{{article.body}}</p>
</div>
<div class="col-md-4">
<h2>老齐教室</h2>
<p>欢迎访问我的网站:www.itdiffer.com</p>
<p>包括各种学习资料</p>
</div>
</div>
</div>
<!-- Bootstrap 核心 JavaScript 文件 -->
<script src="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js" integrity="sha384-aJ21OjlMXNL5UyIl/XNwTMqvzeRMZH2w8c5cRVpzpU8Y5bApTppSuUkhZXN0VxHd" crossorigin="anonymous"></script>
</body>
</html>
接着配置 URL,因为还是针对 book 这个应用而言,所以不需要修改 ./mysite/urls.py
,只需要在 ./book/urls.py
文件中增加新的 URL 路径。
from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^$', views.book_title, name="book_title"),
url(r'(?P<article_id>\d)/$', views.book_article, name='book_content'), # new
]
视图函数 book_article(request, article_id)
的参数 article_id
,用于获得 URL 中每篇文章的 id,即 ./book/templates/book/titles.html
文件中的超链接 <a href='{{title.id}}'>
所产生的请求地址中最后的数字,例如 http://127.0.0.1/blog/1/
。因此,使用 r'(?P<article_id>\d)/$'
这种方式,得到该请求的 id 数值并赋值给参数 article_id
。
现在访问文章标题网页,则所发布的文章已经具有了超级链接(要养成习惯,每次测试效果时,都要查看 Django 是否启动了),点击该标题,即可显示如图12-3-8所示的文章详情页。
图12-3-8 文章详情页
至此,以不求甚解的精神和实践,制作了一个非常简陋的网站——严格地说,是编写了网站源码。要想让这个网站能够成为万维网上的一员,还需要做很多工作,不过那些不是本书要介绍的了,有兴趣的读者可以参考有关资料。
★自学建议 制作网站,是软件开发中一个非常普遍的领域,又由于网站的用途和功能、性能要求各异,也出现了各类不同的技术形态,每种技术还会对应专门化的岗位及具有相应专业技能的从业者。
以上仅仅列出制作和发布网站所涉及到的主要方面,还有很多其他内容,比如网站源码管理、网站测试、网站开发流程、网站功能设计等。总而言之,制作网站是一项综合性很强的系统工程,需要多个技能配合应用(大型项目就把每个技能落实到专业人员上)。 有志于从事 Web 开发的读者,不妨参考上述介绍,在自学完本书内容之后,向着选定的专业技能方向发展。 如果读者愿意以 Django 框架作为 Web 开发的学习起点(这个选择很明智),可以参阅拙作《跟老齐学Python:Django 实战(第二版)》(电子工业出版社)。