习题 50: 你的第一个网站
1. 安装lpthw.web
在创建网页应用程序之前,需要安装一个“Web框架”,叫做lpthw.web。
所谓“框架”,通常是指“让某件事情做起来更容易的软件包”。在网页应用的世界里,人们创建了各种各样的“网页框架”,用来解决他们在创建网站时遇到的问题,然后把这些解决方案用软件包的方式发布出来,这样你就可以利用它们引导创建你自己的项目了。
$ sudo pip install lpthw.web
注意⚠️:直接执行上述命令下载lpthw.web的可能会报以下错误:
Could not find a version that satisfies the requirement lpthw.web
解决方案:
1. 下载[lpthw.web-1.1.tar.gz](https://files.pythonhosted.org/packages/52/0d/a200087bb551895143fedfb5f7df416ccb7ecb46428e04dd011a7ea19689/lpthw.web-1.1.tar.gz)
2. 解压,并将文件拷贝到pip的第三方库的安装目录,参考:/usr/local/lib/python2.7/site-packages(可以通过pip show nose命令查看)
3. cd进入到文件夹下,执行命令: $sudo python setup.py install
(PS:以上是 Linux 和 Mac OSX 系统下的安装命令)
2. 写一个简单的“Hello World”项目
现在你将作出一个非常简单的‘hello world’项目出来,首先需要创建一个项目目录:
$ cd projects
$ mkdir gothonweb
$ cd gothonweb
$ mkdir bin gothonweb tests docs templates
$ touch gothonweb/__init__.py
$ touch tests/__init__.py
将下面的代码放到app.py中:
import web
urls = (
'/', 'index'
)
app = web.application(urls, globals())
class index(object):
def GET(self):
greeting = "Hello World"
return greeting
if __name__ == "__main__":
app.run()
使用下面的方法来运行这个web程序:
$ python bin/app.py
注意⚠️:如果出现下方这种提示,说明8080端口被占用(后来发现是Nginx占用了8080端口,关掉Nginx就好了):
no socket could be created
如果没有出现问题,就会出现下面的提示:
http://0.0.0.0:8080/
127.0.0.1:50947 - - [16/Apr/2019 19:28:31] "HTTP/1.1 GET /" - 200 OK
这些是lpthw.web打印出的 log 信息,从这些信息你可以看出服务器有在运行。
发生了什么事情?
在浏览器访问到你的网页应用程序时,发生了下面一些事情:
- 浏览器通过网络连接连接到你自己的电脑,它的名字叫做localhost,这是一个标准称谓,表示的谁就是网络中你自己的这台计算机,不管它实际名字是什么,你都可以使用localhost来访问。它使用的网络端口是5000。
- 连接成功以后,浏览器对bin/app.py这个应用程序发出了HTTP请求(request),要求访问URL/,这通常是一个网站的第一个URL。
- 在bin/app.py里,我们有一个列表,里面包含了URL和类的匹配关系。我们这里只定义了一组匹配,那就是‘/’,‘index’的匹配。他的含义是:如果有人使用浏览器访问/这一级目录,lpthw.web将找到并加载class index,从而用它处理这个浏览器请求。
- 现在lpthw.web找到了class index,然后针对这个类的一个实例调用了index.GET这个方法函数。该函数运行后返回一个字符串,以供lpthw.web将其传递给浏览器。
- 最后lpthw.web完成了对于浏览器请求的处理,将相应回传给浏览器,于是你就看到了现在的页面。
修正错误
第一步,把第 11 行的greeting变量赋值删掉,然后刷新浏览器。你应该会看 到一个错误页面,你可以通过这一页丰富的错误信息看出你的程序崩溃的原因是 什么。当然你已经知道出错的原因是 greeting 的赋值丢失了,不过 lpthw.web 还是会给你一个挺好的错误页面,让你能找到出错的具体位置。 试试在这个错误页面上做以下操作:
- 检查每一段 Local vars 输出(用鼠标点击它们),追踪里边提到的变量名称,以 及它们是在哪些代码文件中用到的。
网页错误提示:
| self | <app.index object at 0x103446750>
<bound method index.GET of <app.index object at 0x103446750>>
终端错误提示:
Traceback (most recent call last):
File "/usr/local/lib/python2.7/site-packages/lpthw.web-1.1-py2.7.egg/web/application.py", line 236, in process
return self.handle()
File "/usr/local/lib/python2.7/site-packages/lpthw.web-1.1-py2.7.egg/web/application.py", line 227, in handle
return self._delegate(fn, self.fvars, args)
File "/usr/local/lib/python2.7/site-packages/lpthw.web-1.1-py2.7.egg/web/application.py", line 409, in _delegate
return handle_class(cls)
File "/usr/local/lib/python2.7/site-packages/lpthw.web-1.1-py2.7.egg/web/application.py", line 384, in handle_class
return tocall(*args)
File "/Users/douyueyue/study/python_practise/projects/gothonweb/bin/app.py", line 11, in GET
return greeting
NameError: global name 'greeting' is not defined
-
阅读 Request Information 一节,看看里边哪些知识是你已经熟悉了的。 Request 是浏览器发给你的gothonweb应用程序的信息。这些知识对于日常网页 浏览没有什么用处,但现在你要学会这些东西,以便写出 web 应用程序来。
试着把这个小程序的别的位置改错,探索一下会发生什么事情。
lpthw.web
的会 把一些错误信息和堆栈跟踪(stack trace)信息显示在命令行终端,所以别忘了检查命 令行终端的信息输出。
创建基本的模板文件
这是一个 web 应用,所以需要一个合适的 HTML 响应页面才对。为了达到这个目的,下一步你要做的是将“Hello World” 以较大的绿色字体显示出来。
第一步是创建一个templates/index.html文件,内容如下:
$def with (greeting)
<!DOCTYPE html>
<html>
<head>
<title>Gothons Of Planet Percal #25</title>
</head>
<body>
$if greeting:
I just wanted to say <em style="color: green; font-size: 2em;">$greeting</em>.
$else:
<em>Hello</em>, world!
</body>
</html>>
这里的HTML文件其实是个“模板(template)”,如果你向模板提供一个参数,lpthw.web就会在模板中找到对应的位置,将参数的内容填充到模板中去。例如index.html中每一个出现$greeting的位置,$greeting的内容都会被替换成对应这个变量名的参数。
为了让你的bin/app.py处理模板,你需要写一写代码,告诉lpthw.web到哪里去找到模板进行加载,以及如何渲染(render)这个模板,按照下面的方式修改你的app.py:
import web
urls = (
'/', 'index'
)
app = web.application(urls, globals())
render = web.template.render('templates/')
class index(object):
def GET(self):
greeting = "Hello World"
return render.index(greeting = greeting)
if __name__ == "__main__":
app.run()
特别注意一下render这个新的变量名,我修改了index.GET的最后一行,让它返回了render.index(),并且将greeting变量作为参数传递给了这个函数。
改好上面的代码后,刷新网页,输出如下:
可以在浏览器中通过“查看源文件(view Source)”看到模板被渲染成了标准有效的HTML源代码:
<!DOCTYPE html>
<html>
<head>
<title>Gothons Of Planet Percal #25</title>
</head>
<body>
I just wanted to say <em style="color: green; font-size: 2em;">Hello World</em>.
</body>
</html>>
模板的工作原理
- 在bin/app.py里面添加的一个叫做render的新变量,它本身是一个web.template.render对象。
- 你将templates/作为参数传递给了这个对象,这样就让render知道了从哪里去加载模板文件。
- 在你后面的代码中,当浏览器一如既往的触发了index.GET以后,它没有再返回简单的greeting字符串,取而代之的是你调用了render.index,而且将问候语句作为一个变量传递给它。
- 这个render_template函数可以说是一个“魔法函数”,它看到了你需要的是index.html,于是跑到templates/目录下,找到名字为index.html的文件,然后就把它渲染(render)一遍。
- 在templates/index.html文件中,你可以看到初始定义一行中说这个文件需要使用一个叫做greeting的参数,这和函数定义中的格式差不多。另外和Python语法一样,模板文件是缩紧敏感的。
- 最后,你让templates/index.html去检查greeting这个变量,如果这个变量存在的话,就打印出变量的内容,如果不存在的话,就会打印出一个默认的问候信息。
加分题
- 到http://webpy.org/阅读里边的文档,它其实和lpthw.web是同一个项目。
- 实验一下你在上述网站看到的所有的东西,包括里边的代码示例。
- 阅读以下 HTML5 和 CSS3 相关的东西,自己练习着写几个 .html 和 .css 文件
- 如果你有一个懂 Django 朋友可以帮你的话,你可以试着使用 Django 完成一下习题 50、51、52,看看结果会是什么样子的。
首先安装Django:
Python环境与Django版本对应表:
参考:https://www.jianshu.com/p/9af73e184569
从这里下载:DJango-1.x.y.tar.gz,在页面右侧列表下载:
下载后进入下载目录,双击解压
进入解压后的目录:
$ cd Django-1.x.y
$ sudo python setup.py install
成功后提示:
Processing dependencies for Django==1.x.y
Finished processing dependencies for Django==1.x.y
进入我们的项目目录,创建 Django 项目:
$ django-admin.py startproject testdj
启动服务:
cd testdj # 切换到我们创建的项目
$ python manage.py runserver
……
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
以上信息说明,项目已启动,访问地址为http://127.0.0.1:8000/。
可以来看下django-admin 的命令介绍:
$ django-admin
Type 'django-admin help <subcommand>' for help on a specific subcommand.
Available subcommands:
[django]
check
compilemessages
createcachetable
dbshell
diffsettings
dumpdata
flush
inspectdb
loaddata
makemessages
makemigrations
migrate
runserver
sendtestemail
shell
showmigrations
sqlflush
sqlmigrate
sqlsequencereset
squashmigrations
startapp
startproject
test
testserver
Note that only Django core commands are listed as settings are not properly configured (error: Requested setting INSTALLED_APPS, but settings are not configured. You must either define the environment variable DJANGO_SETTINGS_MODULE or call settings.configure() before accessing settings.).
使用 django-admin 来创建 HelloWorld 项目:
django-admin startproject HelloWorld
创建完成后我们可以查看下项目的目录结构:
目录说明:
HelloWorld: 项目的容器。
manage.py: 一个实用的命令行工具,可让你以各种方式与该 Django 项目进行交互。
HelloWorld/init.py: 一个空文件,告诉 Python 该目录是一个 Python 包。
HelloWorld/settings.py: 该 Django 项目的设置/配置。
HelloWorld/urls.py: 该 Django 项目的 URL 声明; 一份由 Django 驱动的网站"目录"。
HelloWorld/wsgi.py: 一个 WSGI 兼容的 Web 服务器的入口,以便运行你的项目。
在 HelloWorld 目录底下创建 templates 目录并建立 hello.html文件,整个目录结构如下:
hello.html 文件代码如下
<h1>I want to say:{{ hello }}</h1>
从模板中我们知道变量使用了双括号。
接下来我们需要向Django说明模板文件的路径,修改HelloWorld/settings.py,修改 TEMPLATES 中的 DIRS 为 [BASE_DIR+"/templates",],如下所示:
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [BASE_DIR+"/templates",], # 修改位置
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
修改 view.py,增加一个新的对象,用于向模板提交数据:
from django.shortcuts import render
def hello(request):
context = {}
context['hello'] = 'Hello World! yeah~'
return render(request, 'hello.html', context)
可以看到,我们这里使用 render 。render 还使用了一个字典 context 作为参数。context 字典中元素的键值 "hello" 对应了模板中的变量 "{{ hello }}"。
再访问访问 http://127.0.0.1:8000/hello,可以看到页面