Python专题(四) 如何制作一个demo给老板看

2020-06-23 00:00:00 函数 文件 后端 页面 交互

用了几个月做了一个项目,肯定要好好给老板展示一下。你会怎么做呢?是不是立马开始截图准备做PPT了呢?PPT虽好,但是也只能给老板展示一些静态图片和视频,并不能实时展示你的功能。那么我们可以用Python做一个简单的网页来展示我们的工作成果。

本文就分享一下如何用轻量级Web框架Flask+HTML+JavaScript来做一个网页端应用。

1
Flask

Flask是一个轻量级的后端应用框架,特点是简单易用、灵活轻便。由于拥有这些特点,近些年Flask迅速成为Python端受欢迎的Web框架之一。有了Flask做帮手,你可以轻松地搭建一个自己的Web后端。

接下来,我们来看看Flask究竟如何使用。

首先,为了避免与其他环境和项目产生版本冲突,我们为这个新项目开一个新的环境(细节可参考Python专题(一)),本文的环境为Python3.6。其次,为这个环境安装必要的依赖包,例如Flask以及其他可能用到的安装包。准备就绪后,新建一个Python文件,以 app.py为例。

  1. from flask import Flask

  2. app = Flask(__name__)

  3. @app.route("/")

  4. def home():

  5.  return "hello world"

  6. if __name__ == "__main__":

  7.  app.run('0.0.0.0', port=8888)

现在就完成了一个简单的hello world的页面的后端,是不是简单到不可思议?那就试试。

  1. python app.py

然后打开浏览器输入 0.0.0.0:8888/,就可以看到如下界面:

不到10行代码,就搭成了一个简单的Web helloworld,flask的确简单轻便。那么我们再来探究一下这短短几行代码背后的原理。

首先,要初始化一个 Flask对象,这个对象就抽象了整个Web后端部分。所以后面所有关于后端的操作的入口都是这个对象。接下来我们定义了 home函数,这是一个装饰器函数,由 app.route("/")这个有参装饰器装饰。被这个装饰器装饰的函数当后端"/"这个页面被访问时将会被调用。简单来说就是, app.route(x)这个装饰器将会定义访问页面时进行的操作。在上述例子中,当我们在浏览器中访问 0.0.0.0:8888/的时候,Flask对象会执行 home函数,也就是返回字符串 hello world,这个字符串在浏览器中会被当做HTML语言来处理,因此会在页面中显示 hello world。Flask之所以简单易用也是得益于装饰器函数的巧妙设计,让装饰器函数帮你做了重复以及冗长的底层操作,而你只需要定义核心功能。这种方式是不是有种似曾相识呢?在《Python专题(二)Python二三事》中我们介绍了一种Python2和Python3代码自动转换的工具,它里面也是大量用装饰器函数来实现各种转换操作的。

那接下来看看 route()这个装饰器函数究竟做了些什么吧。

  1.  def route(self, rule, **options):

  2.  """A decorator that is used to register a view function for a

  3.        given URL rule.  This does the same thing as :meth:`add_url_rule`

  4.        but is intended for decorator usage::

  5.            @app.route('/')

  6.            def index():

  7.                return 'Hello World'

  8.        For more information refer to :ref:`url-route-registrations`.

  9.        :param rule: the URL rule as string

  10.        :param endpoint: the endpoint for the registered URL rule.  Flask

  11.                         itself assumes the name of the view function as

  12.                         endpoint

  13.        :param options: the options to be forwarded to the underlying

  14.                        :class:`~werkzeug.routing.Rule` object.  A change

  15.                        to Werkzeug is handling of method options.  methods

  16.                        is a list of methods this rule should be limited

  17.                        to (``GET``, ``POST`` etc.).  By default a rule

  18.                        just listens for ``GET`` (and implicitly ``HEAD``).

  19.                        Starting with Flask 0.6, ``OPTIONS`` is implicitly

  20.                        added and handled by the standard request handling.

  21.        """

  22.        def decorator(f):

  23.            endpoint = options.pop("endpoint", None)

  24.            self.add_url_rule(rule, endpoint, f, **options)

  25.            return f

  26.        return decorator

其实也很简单,就是提供了一个装饰器帮你给被装饰的函数添加一个URL规则。但也就是这种装饰器的使用,让你仅需要一行代码一个参数就可以定义好一个页面的响应。

继续说回开始那7行代码,后一行的 app.run('0.0.0.0',port=8888)会在你指定的ip和端口上运行这个Flask后端应用。这样,当我们执行 app.py的时候,这个后端应用就可以在 0.0.0.0:8888这个地址上使用了。

了解了以上7行代码的运行逻辑和原理,你就可以根据需求在指定的路由下定制响应。例如,你需要一个上传图片的功能:

  1. import flask

  2. from hashlib import sha1

  3. from PIL import ImageFile

  4. @app.route('/files', methods=['POST'])

  5. def upload():

  6.  sha1sum = sha1(flask.request.data).hexdigest()

  7.  target = "static/{0}.png".format(sha1sum)

  8.  image_parser = ImageFile.Parser()

  9.  image_parser.feed(flask.request.data)

  10.  image = image_parser.close()

  11.  image.save(target)

以上代码中有两个需要注意的地方。是这个函数是如何与前端的请求交互的,可以注意到在route方法的参数中,我们多指定了一个 methods叫做 POST请求,这样 upload函数就会在URL指定在 /files时前端传来 POST请求时被执行。与 POST请求相似的还有 GET请求,一般用于前端获取后端数据,因此我们还可以在 /files下再定义一个 GET请求,来实现另一个功能。第二是关于这个函数是如何与前端上传的数据进行交互的,可以看到在函数中,我们使用了 flask.request.data变量,这个变量就是flask帮助我们收集的前端request的数据内容,这也就是 upload函数与前端上传数据交互的接口,后面的一些处理都是为了将二进制或者十六进制的数据解析成图片保存在服务器中。这样我们又通过一个例子搞明白了前后端的请求和数据交互的过程,已经具备了完成一个简单Web demo后端的基础知识了。

2
HTML + JavaScript

有了Flask提供后端计算、IO支持还不够,你还需要一个前端页面来展示结果。如果你想做一个非常酷炫前端逻辑比较复杂的页面,那可能你需要入门React等其他比较新的前端框架。但是如果你只需要一个简单朴素的页面来展示结果,那么HTML和JavaScript的完全可以满足需求,而且更加容易上手。

关于HTML和JavaScript的基础知识限于篇幅,本文不再赘述。不过笔者的实际操作经验告诉你们 https://www.w3schools.com/这个网站基本包括了所有前端的基础页面、功能以及控件的例程。这些例子是完全可以满足非专业前端的一个小小demo的需求的。如下图,你想做一个图片展示的静态页面,根据网站给你的模板去做一些修改即可,便捷易上手。

所以说,在前端这部分,你所需要做的工作就是整理一下自己的需求,需要实现几个功能,几个页面,每个页面要长什么样子。如果你有前端基础,可以根据设计自己去实现;如果没有前端基础,那就根据要求按图索骥把w3schools上的模板拼在一起实现功能。当然,戏说不是胡说,改编不是瞎编,即使是拼凑,也是要有一个方法论的。下图展示了笔者在做此类前端页面时总结的方法论。以本文所提到的技术栈来说,前端大致可以分为3个部分,分别是HTML,CSS以及JavaScript。其中HTML是顶层的文件,它定义了静态页面(页面长什么样儿),以及前后端交互时的响应函数(一般由JavaScript来实现,在html中调用)。CSS是辅助定义静态页面风格的,因为在静态页面定义中会有非常多的冗余代码,CSS可以把静态页面中的元素进行分组管理,这样就不用对每个元素进行属性设置,减少了HTML中大量冗余的代码。JavaScript提供了前后端交互响应函数的实现。明白了这个架构,改写前端的代码时,就可以如下图所示分为 .html文件, .css文件以及 .js文件,根据不同的作用分开改写,这样方便整个工程的管理,逻辑上也更加清晰。

有了方法论,接下来再看看前端部分要如何与后端交互呢?

3
前后端交互

变量交互

还记得Flask那一节中的 home函数么,当我们在浏览器中访问 0.0.0.0:8888/页面时,页面显示了 hello world,也就是 home函数的返回值。因此可以想见,如果我们让 home返回一个包含完整HTML内容的字符串,浏览器就可以帮我们显示出一个更优美的页面。

例如,我们把 home函数改为:

  1. @app.route("/")

  2. def home():

  3.    return """<!DOCTYPE html>

  4. <html>

  5. <body>

  6. <h2>An Unordered HTML List</h2>

  7. <ul>

  8.  <li>Coffee</li>

  9.  <li>Tea</li>

  10.  <li>Milk</li>

  11. </ul>  

  12. <h2>An Ordered HTML List</h2>

  13. <ol>

  14.  <li>Coffee</li>

  15.  <li>Tea</li>

  16.  <li>Milk</li>

  17. </ol>

  18. </body>

  19. </html>"""

再次运行 app.py就会看到这样的页面:



这种方式尽管可以让我们完成一个简单页面的搭建,但是Python的代码中返回这么长一段字符串,看起来还是非常不优雅的。更重要的是,当HTML中存在需要跟后端交互的变量时,这种返回字符串的做法就非常不方便了。

好在,Flask帮我们实现了一个 render_template的方法,他允许我们从工程中的特定文件夹 templates中选取HTML文件作为函数的返回值。所以现在,在项目根目录下新建一个 templates文件夹,然后在 templates下新建一个 test.html文件,写入HTML语句:

  1. <!DOCTYPE html>

  2. <html>

  3. <body>

  4. <h2>An Unordered HTML List</h2>

  5. <ul>

  6.  <li>Coffee</li>

  7.  <li>Tea</li>

  8.  <li>Milk</li>

  9. </ul>

  10. <h2>An Ordered HTML List</h2>

  11. <ol>

  12.  <li>{{order_name1}}</li>

  13.  <li>{{order_name2}}</li>

  14.  <li>{{order_name3}}</li>

  15. </ol>

  16. </body>

  17. </html>

注意,当前我们的项目路径就变为了:

| project

| ----templates

| ---------test.html

| ----app.py

render_template会自动在template路径下索引文件。

接下来修改home`函数为:

  1. from flask import render_template

  2. @app.route("/")

  3. def home():

  4.    return render_template("test.html",  # render_template会自动在template路径下索引文件

  5.                           order_name1="Cola",  # 通过render_template可以方便地为HTML添加变量

  6.                           order_name2="Water",

  7.                           order_name3="Tea"

  8.                          )

再次运行 app.py就可以看到如下界面:

可以看到通过render_template,我们可以很方便地为HTML文件加入后端可以控制的变量,从而完成了变量的前后端交互。

静态文件管理

对于一个Web应用来说,往往还需要跟前端交互一些文件内容,例如上传或者下载文件等操作。Flask也提供了一些文件管理的能力,首先要明确的是,由于Web应用需要保证服务器上的文件安全,所以Flask后端只能访问指定文件夹下的内容,默认情况下,这个指定文件夹为 static,可以通过

  1. app = Flask(__name__, static_folder=static/path/you/want)

static_folder变量来修改指定文件夹。此时工程目录就变成了:

| project

| ----templates

| ---------test.html

| ----static

| --------logo.png

| ----app.py

此时,我们在后端是可以访问到 static目录下的 logo.png这张图片的。在Flask那一节中我们举了一个图片上传功能实现的例子,那个例子中我们上传的图片或者其他数据也都会保存在这个 static目录下。当然,既然后端可以保存和访问 static目录下的文件,那么前端也自然可以通过变量传递显示 static目录下的文件。下面的例子展示了前端访问 static/logo.png的结果:

至此,就搞明白了用Flask做一个Web应用的基本原理了,后我把这些内容整理在一张图中,方便串联各个模块的逻辑关系。个人感觉,写Flask的项目和普通项目大的区别就是,你需要时刻注意前后端的变量、文件、数据的交互,在开始上手时,我困惑的就是他们的数据是如何传递和交互的,但是当我整理和总结了整个原理和流程后,就发现整个项目的逻辑和架构更加清晰了,自己也理解了数据的流向和数据的传递过程。

有了这些基础,完全可以做一个简单的Web Demo了,你不防也动手一试。

往期精彩回顾
【Python专题(一)】python环境搭建
【Python专题(二)】Python二三事
【Python专题(三)】Python模块导入与路径管理
我就知道你“在看”

相关文章