2017年2月14日07:32:58

近些天显得有些漫不经心,主要体现在对开智学堂微信群里面内容关注较少,热情相比没有以往高,所以自然在作业的跟进上面不够积极,特别在当前作业还处于延迟的情况之下。

这是为什么呢?重新评估一下:

  • 之前决定学习Python的出发点是什么?
  • 目前手里各种事情的重要性是怎样的?Python学习排在什么位置?

审视之下其实发现Python学习的重要性依然高,但因为似乎当前不够“紧急”而没有时刻跟踪处理它。调整一下,否则任务将越来越拖延了。

昨天晚上阅读《程序员思维修炼》,其中提到指定目标时候的SMART原则,刚好可以在这里使用一次:

  • Specific: 完成教练指定的ch4工作,并提交作业。
  • Measurable:当前还剩下三个子任务,逐个完成:
    • Object4.3: {S:与服务器交互; M:阅读文档/编写代码;A:按照一个子任务4个番茄,约2小时时间可完成;R:自己想学习,同侪压力;T:预计在15晚上12点之前。}
    • Object4.4: {S:使用template; M:阅读文档/编写代码;A:按照一个子任务4个番茄,约2小时时间可完成;R:自己想学习,同侪压力;T:预计在17晚上12点之前。}
    • Object4.5: {S:使用CSS; M:阅读文档/编写代码;A:按照一个子任务4个番茄,约2小时时间可完成;R:自己想学习,同侪压力;T:预计在18晚上12点之前。}
  • Achieveable: 当前一个子任务约2小时时间,还剩下三个子任务,需要6小时时间。
  • Relevant: 这个任务是自己主动学习的实验,以后希望可以使用Python进行Web/数据分析,所以很重要。
  • Time-boxed: 周五(18日)晚上12点之前提交任务。

依然从阅读参考文档开始,首先阅读HTTP Methods: GET vs. POST,在之前阅读What is a Web Framework?的时候曾经提到过GET/POST方法的区别,那那这里有什么不同呢?之前理解的时候都是看它们是否改变了Server端的状态来区分,在这篇参考文档里面则是从Client端区分它们:

  • GET - Requests data from a specified resource GET方法一般用于检索数据,传送简单的检索request,它携带的request数据与URL一起发送。

  • POST - Submits data to be processed to a specified resource POST方法通常用于给Server发送特定的数据,并且这些数据会触发Server的一些特定操作,这些数据会存放在POST里面HTTP消息体当中。

准备Server

2017年2月15日20:22:40

唔,好吧,项目跟得紧,都没空学Python了,抽时间继续。

开始阅读第二篇参考文章:HTTP Methods - Flask。这一篇其实再第一个子任务当中已经涉及到了,当时是执行了一个最简单的Flask应用,打印了Hello World。当时的第一步是启动服务端example_hello.py,然后在浏览器当中通过127.0.0.1:5000来访问。目前已经设计好了一个index页面,那么第一个问题便是:怎样在访问服务器的时候让这个index.html的效果显示出来?

考虑如下示例代码,我们如何修改hello_world()函数,或者说怎样编写一个新的函数返回Section4_3_index.html中的内容并替换掉示例代码当中的hello_world()。

from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello_world():
    return 'Hello, World!'

在阅读文档中“Rendering Templates”一节之后我的尝试很简单,两步:

  • 按照要求调整目录结构,新建Templates目录,并将index.html文件放进去。
  • 修改如上示例代码用来当做Server端逻辑:

      from flask import Flask
      from flask import render_template
    
      app = Flask(__name__)
    
      @app.route('/')
      def index():
          return render_template('index.html')
    

访问效果:

怎么回事?我的意图很简单,只需要直接返回一个html文件内容就好了呀。查看错误日志:

return self.get_template(template_name_or_list, parent, globals)
File "d:\learning\git\py103\chap4\project\venv\lib\site-packages\jinja2\environment.py", line 830, in get_template
return self._load_template(name, self.make_globals(globals))
File "d:\learning\git\py103\chap4\project\venv\lib\site-packages\jinja2\environment.py", line 804, in _load_template
template = self.loader.load(self, name, globals)
File "d:\learning\git\py103\chap4\project\venv\lib\site-packages\jinja2\loaders.py", line 113, in load
source, filename, uptodate = self.get_source(environment, name)
File "d:\learning\git\py103\chap4\project\venv\lib\site-packages\flask\templating.py", line 57, in get_source
return self._get_source_fast(environment, template)
File "d:\learning\git\py103\chap4\project\venv\lib\site-packages\flask\templating.py", line 85, in _get_source_fast
raise TemplateNotFound(template)
TemplateNotFound: index.html
127.0.0.1 - - [15/Feb/2017 20:58:48] "GET / HTTP/1.1" 500 -

是“TemplateNotFound”问题,仔细确认自己文件目录发现index.html没有放到templates里,调整之后显示成功!

使能功能

2017年2月18日09:51:12

这周实在有点忙,白天无法抽空,晚上累得够呛。因此在14日制定的目标没有很好达成,争取在这两天完成ch04的作业。

15日已经将Server端的页面准备好,下一步是一步一步连接功能:如何通过点击浏览器上的按钮来从Server端获取数据。准备先完成“帮助”功能。

按照web浏览器与Server的请求<->响应通信模型,“帮助”功能的过程包括:

  • ①用户点击“帮助”按钮。
  • ②浏览器将“帮助”按钮的动作传送给Server。
  • ③Server接收请求。
  • ④Server组装响应结果。
  • ⑤Server将响应结果传送回浏览器。

这个过程当中的第②、③与④步都需要开发人员解决。

1.点击按钮,给Server发送请求。

记得在学习 HTML Input Types - w3school的时候曾经看到过定义form时可以指定它的action,这个意思其实就是点击按钮的时候会触发这个动作,不过对于这个动作的认识不够具体。所以我跳转到HTML Forms阅读更详细的介绍,并且依据其中的例子重新改写了index.html中form部分的代码:

<form style="text-align:center;" action="/user_request" method="get">
  城市:
  <input type="text" name="query_city">
  <input type="submit" value="查询">
  <input type="submit" value="历史">
  <input type="submit" value="帮助">
</form>

在form定义当中添加了action="/user_request" method="get",然后重新启动Server,测试一次,效果如下:

根据错误提示联想推测点击“帮助”按钮的时候,浏览器会尝试访问Server端的user_request页面,然后把用户请求的数据query_city带给它。

这样就自动过度到第③步。

2.在Server添加用户请求处理流程

既然上面提到在Server上找不到对应页面,那么尝试在Server端新建query_city,如下:

from flask import Flask
from flask import render_template

app = Flask(__name__)

@app.route('/')
def index():
    return render_template('index.html')

@app.route('/user_request')
def process_request():
    return "hello, world"

也就是新增加了下方那一段代码,测试效果:

很显然,这应征了自己的猜想。那么,这个时候就可以根据用户请求的类型来返回不同的结果了,过度到第④步。

3.根据用户请求类型来返回不同结果。

现在有两个问题需要解决:一是用户点击不同的按钮对应不同的request内容,那么如何在Server端分别它们;二是根据不同的request返回不同的内容,但是当前它们的内容中有公共的部分,也就是index.html当中的内容。如何解决?

不过,先采用MVP原则,先将如上的“帮助”功能实现再说。实现过程是先新建一个返回帮助信息的html:help.html,然后修改Server端代码,在接收到用户请求的时候直接返回这个页面。也就是添加如下代码:

.route('/user_request')
def process_request():
    return render_template('help.html')

问题思考:可以预见,如上使用单独html页面来返回可以实现功能,但是各个html页面中公共的部分该如何处理呢?

  • 建立多个html文件,分别传送。
  • 使用类似python当中的import功能,存在吗?
  • 其他什么方式?

Accessing Request Data - Flask提到了使用request模块来访问POST/GET请求中数据的方法,但仍旧无法得知“如何确定用户点击了什么按钮”。测试了一下,点击三个按钮向服务器发送的GET信息看起来是一样的,但在填写了“城市”之后会不同。

127.0.0.1 - - [18/Feb/2017 11:58:07] "GET /user_request?query_city= HTTP/1.1" 400 -
127.0.0.1 - - [18/Feb/2017 11:58:14] "GET /user_request?query_city= HTTP/1.1" 400 -
127.0.0.1 - - [18/Feb/2017 11:58:35] "GET /user_request?query_city= HTTP/1.1" 400 -
127.0.0.1 - - [18/Feb/2017 11:58:52] "GET /user_request?query_city=%E6%9D%AD%E5%B7%9E HTTP/1.1" 400 -

重新阅读代码发现没有为“帮助”、“历史”和“查询”按钮指定name属性,尝试添加之后发现URL里面的信息就包含了各个按钮的信息:

> flask run
 * Serving Flask app "application"
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
127.0.0.1 - - [18/Feb/2017 13:26:14] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [18/Feb/2017 13:26:17] "GET /user_request?query_city=&help=%E5%B8%AE%E5%8A%A9 HTTP/1.1" 400 -
127.0.0.1 - - [18/Feb/2017 13:26:52] "GET /user_request?query_city=&history=%E5%8E%86%E5%8F%B2 HTTP/1.1" 400 -
127.0.0.1 - - [18/Feb/2017 13:26:57] "GET /user_request?query_city=&query=%E6%9F%A5%E8%AF%A2 HTTP/1.1" 400 -

再次阅读Accessing Request Data - Flask,并查阅了Incoming Request DataMultiDict,尝试从request当中的args参数解析出按钮类型:

oute('/user_request')
def process_request():
    if (request.args.get('query') != None):
        return "query"
    elif (request.args.get('help') != None):
        return "help"
    elif (request.args.get('history') != None):
        return "history"

测试时,点击不同的按钮会输出不同的字符串,因此成功了。那下一步就是将这些字符串替换为对应的html,替换 之后测试如下:

“点击查询”

“点击帮助”

“点击历史”

插入内容

上面的结果只是返回了不同的静态html文件,如何根据用户请求的数据来动态的组装响应结果呢?比如查询出来了天气数据,怎么把它插入到html文件里面返回给用户呢,这个就是下一个任务里面的主要内容了。

results matching ""

    No results matching ""