参考:
Python之flask结合Bootstrap框架快速搭建Web应用_支持bootstrap的python软件-CSDN博客
https://blog.csdn.net/lovedingd/article/details/106696832
Bootstrap 警告框 | 菜鸟教程
https://www.runoob.com/bootstrap/bootstrap-alert-plugin.html
flask框架的Jinja2中怎么动态的传入图片啊? - 清风徐来的回答 - 知乎
https://www.zhihu.com/question/478883018/answer/2052587300
二十:jinja2之加载静态文件 - 向前走。 - 博客园
https://www.cnblogs.com/zhongyehai/p/11784648.html
Flask jinja2.exceptions.TemplateSyntaxError: 语法错误|极客笔记
https://deepinout.com/flask/flask-questions/129_flask_jinja2exceptionstemplatesyntaxerror_expected_token_end_of_print_statement_got_posted.html
环境:
win10 / centos6 , python3
目录
- 背景&问题
- 基础知识
- helloworld
- 解决思路
- 关键点1:如何存放js文件
- 关键点2:动态更新图像文件的路径
- 关键点3:如何放置css文件
背景&问题
临时接到任务,需要写一个网页,该网页接受一个文字输入,向后台发出post请求,后台(linux系统)接收请求后调用一个可执行文件,获取执行结果(包含文字和图片),进行解析后再更新到当前网页中。因为不涉及数据库,也没有太复杂的操作,所以我决定用flask写。
虽然我之前写过django,但那是五年前的事情了,写网站我一直用的是java。所以我花了1h速通flask的基础操作+调试,终于解决了问题。
本文除了有基础知识还有这个问题的解决思路,如果不想看基础知识可以直接跳转到解决思路部分。
基础知识
首先需要安装flask-bootstrap
。
pip install flask-bootstrap
因为是速通,所以直接上例子。相关知识会写在注释里。
helloworld
文件结构:
myflask.py
from flask import Flask, render_template, request
from flask_bootstrap import Bootstrap
import subprocess
import os
# template_folder: store *.html
app = Flask(__name__, template_folder=os.getcwd() + "/static/templates")
bootstrap = Bootstrap(app)
@app.route('/index')
def index():
return render_template('index.html')
@app.route('/')
def index1():
return "hello"
if __name__ == '__main__':
app.run(host="0.0.0.0", port=8080)
# 如果报错的话可以考虑换成
# app.run(debug=True, host="127.0.0.1", port=8080)
注意:查资料的时候有说template_folder
设为""就是当前路径,然后把*.html
放到/static/templates
下就能找到。我试了,找不到。。所以还是选择指明。
index.html
{% extends "bootstrap/base.html" %}
{% block title %}Flask{% endblock %}
{% block navbar %}
<p>带有下拉菜单的标签</p>
<ul class="nav nav-tabs">
<li class="active"><a href="#">Home</a></li>
<li><a href="#">SVN</a></li>
<li><a href="#">iOS</a></li>
<li><a href="#">VB.Net</a></li>
<li class="dropdown">
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
Java <span class="caret"></span>
</a>
<ul class="dropdown-menu">
<li><a href="#">Swing</a></li>
<li><a href="#">jMeter</a></li>
<li><a href="#">EJB</a></li>
<li class="divider"></li>
<li><a href="#">分离的链接</a></li>
</ul>
</li>
<li><a href="#">PHP</a></li>
</ul>
{% endblock %}
注意:base.html相当于一个基础性的模板。我们写的时候先用extend
表示继承,然后用标签提示每块是什么内容。比如被{%- block styles %}
和{%- endblock styles %}
包裹的地方就是写css文件路径的地方(参见关键点3)。这个模板在类似这样的安装路径下:
D:\ProgramData\Anaconda3\Lib\site-packages\flask_bootstrap\templates\bootstrap
执行后控制台输出为:
访问 ip:8080/
访问 ip:8080/index,可以看到bootstrap已经用上了。
解决思路
首先构建一个form,提交按钮的click函数是:
$("#input-design-submit").click(function () {
// get some data
var seq = $(this).attr("data-seq");
if (seq && seq.length>10 && seq.length < 10000) {
$.ajax({
type: 'POST',
url: "/run_command",
data: {
sq: seq
},
success: function (resp) {
// do sth, like unpack, parse
var obj = jQuery.parseJSON(resp);
var code = obj.code;
...
}
});
} else {
return false;
}
});
然后在后台写提交的路由:
curDir = os.getcwd()
@app.route('/run_command', methods=['POST'])
def run_command():
if request.method == 'POST':
#print(request.form)
# make command
cmdStr = "..."
code = 1 # if fail, 0
res = {}
try:
output = subprocess.check_output(cmdStr, shell=True, stderr=subprocess.STDOUT, universal_newlines=True)
# parse output
except subprocess.CalledProcessError as e:
code = 0
# add some data...
res["code"] = code
# convert to json string
return json.dumps(res)
subprocess_check_output()
参数说明:
- command:这是一个字符串变量,包含要执行的命令。例如,command 可能是一个系统命令或者一个调用其他可执行文件的命令。
- shell=True:这个参数告诉 subprocess 模块在一个 shell 中执行命令。如果设置为 False,则 command 将被当作一个可执行文件路径和参数列表来执行,而不是在 shell 中执行。
- stderr=subprocess.STDOUT:这个参数指定将标准错误(stderr)重定向到标准输出(stdout)。这意味着标准错误将会和标准输出合并到一起,以便能够捕获所有输出。
- universal_newlines=True:这个参数告诉 subprocess 将命令的输入和输出处理为文本模式而不是字节模式。这意味着执行结果将会是一个字符串,而不是一个字节串。
- 返回值就是标准输出stdout。
关键点1:如何存放js文件
考虑到国内访问cdn可能会慢,所以把用到的js提前下载好,保存到本地。
首先需要指定static_folder
:
app = Flask(__name__, template_folder = curDir + "/static/templates", static_folder = curDir + "/static")
然后把*js
放到指定好的位置。
在前端引用:
<!--script src="https://cdn.bootcss.com/jquery/1.12.4/jquery.min.js"></script-->
<!--script src="https://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js"></script-->
<script src="{{ url_for('static', filename='jquery.min.js') }}"></script>
<script src="{{ url_for('static', filename='bootstrap.min.js') }}"></script>
关键点2:动态更新图像文件的路径
这个小工具需要经过计算才能产生图像文件,所以在静态html里是不知道的。我采取的是路径映射的办法。
当前端发出post请求,收到回复后,动态加载一个<img>
,大概写作:
const arr = ['<p><img src="', imgsrc, '" width="49%" alt="404"/></p><p>', desc, "</p>"]
$("#output-res").html(arr.join(''));
$("#output-res").show();
其中imgsrc和desc是两个经过解析得到的字符串;output-res
是一个原本隐藏的div
,需要加载图片的时候再显示。
假设imgsrc的格式是:"/output/" + name + ".gif"
,我的图像文件放在python文件根目录/static/output
下,那么只要再写一个路径映射,利用flask包的send_from_directory
函数就可以让图像正确显示了。
需要注意的是,send_from_directory()
需要同时传递路径和文件名。
@app.route('/output/<filename>')
def uploaded_files(filename):
# for img
file_dir = curDir + '/static/output'
return send_from_directory(file_dir, filename)
关键点3:如何放置css文件
放置在block head中:
{%- block head %}
{%- block styles %}
<!-- Bootstrap -->
<!-- 在线的写法 -->
<!--link href="{{bootstrap_find_resource('css/bootstrap.css', cdn='bootstrap')}}" rel="stylesheet"-->
<link href="{{ url_for('static', filename='bootstrap.min.css') }}" rel="stylesheet">
<link href="{{ url_for('static', filename='style.css') }}" rel="stylesheet">
{%- endblock styles %}
{%- endblock head %}
注意:如果想要在保留bootstrap格式的情况下增加新格式,需要保留第一个链接。此时css文件是放在主函数中写的、static路径下的。