51Testing软件测试论坛

 找回密码
 (注-册)加入51Testing

QQ登录

只需一步,快速开始

微信登录,快人一步

手机号码,快捷登录

查看: 2643|回复: 0
打印 上一主题 下一主题

[转贴] 测试开发之路--Flask 之旅 (一)

[复制链接]
  • TA的每日心情
    无聊
    前天 09:05
  • 签到天数: 1050 天

    连续签到: 1 天

    [LV.10]测试总司令

    跳转到指定楼层
    1#
    发表于 2017-3-3 15:29:21 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
    背景
    接触python两周有余了。越来越喜欢这门语言,因为做什么都足够快,不像java那么笨重。虽然它还是那么容易出错。但是构建一个并不复杂的小工具的时候,它显的效率的多。就像海盗一样:快,狠,准。这里我就不比较java和python的优劣了,这个话题太大了,以我的水平也怕评论不好。我们就各自使用自己认为最适合项目的语言就好了。刚学python的第一周先是学习了一下基本语法,环境,IDE(好在有几年的java底子,学的比较快),然后用pytest和allure做了测试的基础架子,准备测试我们提供的python版本的SDK。之后用python重写了一遍环境管理的那一堆shell脚本,比shell少些了很多代码,之后扩展也好,维护也好都方便了很多(后悔这么晚才学python中~~~)。上周末在家开始学习django和flask, 考虑到底选什么作为建站的框架。后来还是选了flask,因为够小,够灵活。我暂时的需要可能不需要数据库之类的东西。所以也就没选什么都包办了的django。于是我拿我们环境管理的需求练手。之前是通过一堆脚本控制的,现在我做一个web,可以通过在页面上点点点就能管理好这些环境。我想一边练习一边记录其中的步骤,给像我一样刚接触的人一个快速上手的指引吧。由于都是业余时间做,所以更新的会比较慢。

    Flask
    这里介绍一下Flask给像我这种刚刚接触的人把。它给我的感觉就是个十分轻量级的,轻量级到令人发指的web框架。用过Flask之后才知道springMVC到底有多重。它使用起来也很简单,不需要任何web容器。 直接按下面的方式写一小段代码就可以了

    1. from flask import Flask
    2. app = Flask(__name__)

    3. @app.route('/')
    4. def hello_world():
    5.     return 'Hello World!'

    6. if __name__ == '__main__':
    7.     app.run(host='0.0.0.0', debug=True, port=9537)
    复制代码
    当你运行这个脚本后,就可以在浏览器访问了。是不是够简单。同样它也支持像django那么大而全的功能,不过这些功能并没有被集成到Flask里面。 而是在你需要的时候通过安装相应的模块来实现。例如支持数据库的orm框架:Flask-SQLAlchemy 支持数据库迁移,升级等功能的模块:Flask_Migrate。 支持页面表单管理的模块:Flask-WTF。 这些都是需要的时候再安装就可以了。

    学习资料
    贴一下我找到的学习资料
    Flask官方文档的中文翻译版本:http://docs.jinkan.org/docs/flask/index.html
    Flask所有扩展模块的文档:http://flask.pocoo.org/extensions/
    一个我觉得比较好的博客:http://www.pythondoc.com/flask-mega-tutorial/database.html#id2

    开始
    OK,我们首先安装Flask吧,直接用pip安装就好了。暂时我们的功能还很简单,不需要其他的扩展模块。先贴一个图片看看我们的项目结构。

    我从上到下开始介绍吧
    env_info是我们所有环境的配置文件,暂时我没有用数据库来保存这些信息。里面都是ini文件,详情不表了。
    lib 是我放置控制docker的脚本的地方。如大家所见,都是些compile,deploy什么的
    web 是我们Flask的根目录
    static是我们放置静态文件的地方,例如css,js,图片什么的。注:这是Flask的默认设置
    templates是我们所有模板的文件,大家可以先理解为所有的html文件都放在这里。注:这是Flask的默认设置
    首先我们先在web目录的init.py文件里写一下我们Flask的配置:
    1. # coding=utf-8

    2. from flask import Flask

    3. app = Flask(__name__)
    4. # 如果想要使用Flask-WTF的表单,需要一个config文件
    5. app.config.from_object('config')
    复制代码
    接着创建一个urls.py文件,用来编写我们的路由和启动命令。就如我们上面的例子一样,我们这样写
    1. if __name__ == '__main__':
    2.     app.run(host='0.0.0.0', debug=True, port=9537)
    复制代码
    之后运行urls.py就算是我们的启动脚本了。然后写一个首页的路由方法。如下:
    1. @app.route('/')
    2. @app.route('/index', methods=['GET', 'POST'])
    3. def index():
    4.     configs = list_all_config()
    5.     return render_template('index.html', configs=configs)
    复制代码
    我们来解释一下吧。 @app.route, 就是我们的路由方法,可以配置多个, 也就是多个url都可以绑定到这一个方法上。 methods参数设定了方法类型。 可以看到方法里面我们取了所有的配置信息(就是env_info中所有的配置信息)。我们希望将所有的配置信息都展示在首页上。 所以取出信息以后,我们使用render_template这个渲染模板的方法,将index.html进行渲染并把configs传递到模板中(在Flask中页面就是模板)。 好了,现在我们去看看这个index.html里是怎么写的吧。
    1. <div class="row clearfix">
    2.         {% for config in configs %}
    3.             <div class="col-md-4 column">
    4.                 <h2>
    5.                     {{ config.name_prefix }}
    6.                 </h2>
    7.                 <p>
    8.                 </p>
    9.                 <p>
    10.                     <a class="btn" href="#">查看详情 »</a>
    11.                     <a class="btn" href="/restart/{{ config.name_prefix }}">部署环境 »</a>
    12.                 </p>
    13.             </div>
    14.         {% endfor %}
    15.     </div>
    复制代码
    这里来看一下Flask内置的Jinjia2的语法吧。流程控制语句使用{% content %} 来表示, 直接把元素输出到页面上使用{{% element %}}。 上面的代码就是说,我循环一下传递进来的configs列表。把每个配置的名字都展示到页面上,然后每个配置都有两个链接,分别是查看配置的详情和部署环境。这个是后话了。 现在如果我们启动web服务,使用浏览器访问就会看到所有的配置都显示在了我们的主页上。 但是还有点不够,那就是我们的页面实在太难看了。 没有任何的布局。 但是我还是个前端的小白,让我重头学一遍css,js什么的太费时间了。 所以我们可以取巧一下,偷懒一下。来给我们的web换个装

    bootstrap

    换装工具就是bootstrap了。 下载地址如下: http://getbootstrap.com/
    我们要把下载下来的文件解压,然后放到static目录下,如图:

    然后我们在index.html加入引用
    1. <meta name="viewport" content="width=device-width, initial-scale=1.0">
    2.     <!-- 引入 Bootstrap -->
    3.     <link href="http://cdn.static.runoob.com/libs/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">

    4.     <!-- HTML5 Shim 和 Respond.js 用于让 IE8 支持 HTML5元素和媒体查询 -->
    5.     <!-- 注意: 如果通过 file://  引入 Respond.js 文件,则该文件无法起效果 -->
    6.     <script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
    7.     <script src="https://oss.maxcdn.com/libs/respond.js/1.3.0/respond.min.js"></script>
    复制代码
    接着我们使用在线的页面生成工具:http://www.runoob.com/try/bootstrap/layoutit/#

    我们用这个工具拖拽好自己的布局之后。就可以点击那个下载。把HTML代码弄到我们的页面里。 然后我们就有了如下的页面了

    好吧,我不具备什么审美天赋。也没下什么大工夫。看着顺眼就行。

    最关键的功能

    好了,到了最关键的功能了,那就是部署一个环境。 所以在我们的首页中才会有这样一个链接:
    1. <a class="btn" href="/restart/{{ config.name_prefix }}">重新启动 »</a>
    复制代码
    现在我们需要为这个链接创造另一个路由方法。如下:
    1. @app.route('/restart/<name>', methods=['GET', 'POST'])
    2. def restart(name):
    3.     workspace = os.path.dirname(os.path.abspath(os.path.dirname(__file__)))
    4.     path = workspace + '/boot.sh'
    5.     config = workspace + '/env_info/' + name + '.ini'
    6.     command = 'bash %s %s' % (path, config)
    7.     result = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    8.     jobs = Jobs.jobs
    9.     if name in jobs.keys():
    10.         jobs[name].kill()
    11.         jobs.pop(name)
    12.     jobs[name] = result
    13.     return render_template('restart.html', name=name)
    复制代码
    这里我们看到了一个用法/restart/。 这个<>中的name就是页面向我们传递的参数。 所以在restart方法里我们才可以使用name这个参数。我们根据name找到相应的配置文件并使用subprocess模块进行调用。由于我们希望这个步骤是异步的,不能再部署过程中页面一直处于卡死的状态。所以使用Popen这个函数来调用。 我们把标准输出和标准错误都重定向到管道中。把这个调用后返回的对象保存在一个全局的dict中。方便之后取log的时候用。 那么现在我们可以在页面上点击链接进行部署了。但是有一个问题,就是我们不知道部署当前是失败了还是成功了。 我们希望能实时的在页面上看到部署的日志, 我们需求页面能实时的显示最新的log而不必刷新页面。

    Jquery Ajax

    我这个前端水平就只能想到这样一个方案了,现在我们去现在一个Jquery.js吧。 同样放到static目录下。

    然后我们再搞一个log页面, 在head标签中我们引入Jquery。
    1. <script type=text/javascript src="{{ url_for('static', filename='jquery.js') }}"></script>
    复制代码
    然后我们加入下面的一段JS:
    1. <script type=text/javascript>
    2.     $SCRIPT_ROOT = {{ request.script_root|tojson|safe }};
    3. </script>


    4. <script type=text/javascript>
    5.     $(function () {
    6.         for (var i = 1; i < 10000; i++) {
    7.             $.get($SCRIPT_ROOT + '/log/{{ name }}', function (data) {
    8.                 $("#log").append(data);
    9.             })
    10.         }
    11.     });
    12. </script>


    13. <dev id="log"></dev>
    复制代码
    我来解释一下上面这段JS。 $.get方法就是动态执行一个请求的方法,后面跟上url。 $("#log")是JS的选择器,用来定位页面元素,意思是找到id为log的元素并执行append的操作。所有的代码的意思就是循环不停的调用这个get方法,并将返回值作为data传递给之后的function,这个function用来定位元素并把日志追加到页面上。 那么现在,我们唯一缺的就是再写这么一个路由方法实时读取日志了。如下:
    1. @app.route('/log/<name>')
    2. def update_log(name):
    3.     # time.sleep(1)
    4.     job = Jobs.jobs[name]
    5.     # error = job.stderr.readline()
    6.     out = job.stdout.readline()
    7.     return '<p>'+out+'</p>'
    复制代码
    看过之前restart方法的代码,我们知道所有的Popen返回的结果对象都放到了一个全局的dict里。 现在我们根据页面AJax传递name把它找出来。然后读取一行日志信息返回。OK,万事大吉。 我不会截动态图~~ 大家见谅。。。
    唠叨两句
    我知道前端的发展现在已经很厉害了,Jquery这种玩意都不知道过时了多久,我们公司的前端都已经在用React.js。可我是没那么多时间去学了。反正我们做的都是给项目内部用的,所以我想不用纠结什么前沿技术了,够用就好。目前我就做了这么多。 大概实现了最主要的几个功能:显示配置,部署环境,实时获取log。之后加上创建配置和修改配置的功能后就基本上达到可用状态了。额,想想在老东家的时候用springMVC为了达到可用状态可是比这多花了不少的时间。所以我现在很喜欢python,在构建这些工具的时候效率更高,更快。 有个小段子是我最开始请教我司写SDK的小伙子的时候,问他你为什么选择用python,他回答说:为了多活两年。 恩,现在我有点理解他为什么这么说了。 之后我会慢慢的完善这个小网站,就当是练手了,然后我会慢慢的把过程都记录下来发到社区里,一个是对我的学习和工作有个记录,再一个也希望对一些同学有所帮助。毕竟在工作中做测试平台,测试工具什么的可能都要封装成一个web。由于这纯是我的个人意愿,组织上并没有给我在工作中安排时间去做。 所以我都是抽空搞一搞,进度可能有点慢。希望之后能成熟到给一些非技术人员,例如产品,售前的同学去搭建一个他们需要的环境,而不再需要懂技术的人跑去给他们运行脚本部署。恩~ 保持学习
    发表于TesterHome

    本帖子中包含更多资源

    您需要 登录 才可以下载或查看,没有帐号?(注-册)加入51Testing

    x
    分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
    收藏收藏
    回复

    使用道具 举报

    本版积分规则

    关闭

    站长推荐上一条 /1 下一条

    小黑屋|手机版|Archiver|51Testing软件测试网 ( 沪ICP备05003035号 关于我们

    GMT+8, 2024-11-23 08:49 , Processed in 0.064134 second(s), 24 queries .

    Powered by Discuz! X3.2

    © 2001-2024 Comsenz Inc.

    快速回复 返回顶部 返回列表