说明
搭建网站模板的原因是未来需要快速的启动多个不同的web服务,建立一个基于自有算网的分布式web服务。
本篇梳理一下网站的模板(结构及静态资源),未来新的网站可以把模板拷贝过去再进行个性化调整就可以了。
1 结构
首先使用函数生成docker-compose项目结构。
import DataManipulation as dm
# base_prj2 建立基础的web模板
dm.make_folder_struct(project_name='base_prj2', appname='base_web')
模板生成时,模型文件是空的,这里尝试使用jinja的方式写入。未来会形成几套模板,对应生成项目时就可以直接写入内容,表面手工的文件操作。(同理,系统的设置也将如此)
jinja的写入这里就不细说了,下次在别的地方专门说。
2 资源
资源的加载是有顺序的,本例用到了以下资源,都是耳熟能详的,就不解释了。
- 1 网页图标
<link rel="shortcut icon" href="/static/img/tem_logo.ico" type="image/x-icon">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
- 2 Bootstrap4
<link rel="stylesheet" href="/static/vendor/Bootstrap/css/bootstrap.min.css">
<script src="/static/vendor/jquery/jquery.js"></script>
<script src="/static/vendor/Bootstrap/js/bootstrap.min.js"></script>
<script src="/static/vendor/Bootstrap/js/popper.min.js"></script>
-3 DataTables
<link rel="stylesheet" href="/static/vendor/DataTables/css/dataTable.css">
<script src="/static/vendor/DataTables/js/dataTable.js"></script>
<!-- 3.1下载数据表的插件 -->
<script src="/static/vendor/DataTables/js/dataTables.buttons.min.js"></script>
<script src="/static/vendor/DataTables/js/buttons.html5.min.js"></script>
<!-- 3.2下载数据表的插件 -->
<script src="/static/vendor/ajax/pdfmake.min.js"></script>
<!-- vfs_fonts要在pdfmake之后导入,否则会找不到字体 -->
<script src="/static/vendor/ajax/vfs_fonts.js"></script>
<script src="/static/vendor/ajax/jszip.min.js"></script>
- 4 inputfile
<link href="/static/vendor/fileinput/css/fileinput.min.css" media="all" rel="stylesheet" type="text/css" />
<script src="/static/vendor/fileinput/js/plugins/sortable.js" type="text/javascript"></script>
<script src="/static/vendor/fileinput/js/fileinput.min.js" type="text/javascript"></script>
<script src="/static/vendor/fileinput/js/locales/zh.js" type="text/javascript"></script>
<script src="/static/vendor/fileinput/themes/explorer-fa/theme.js" type="text/javascript"></script>
<script src="/static/vendor/fileinput/themes/fa/theme.js"></script>
- 5 FontAweson 要放在inputfile之后
<link rel="stylesheet" href="/static/vendor/FontAwesome/css/font-awesome.min.css">
<link href="/static/vendor/fileinput/themes/explorer-fa/theme.css" media="all" rel="stylesheet" type="text/css" />
- 6 echarts
<script src="/static/vendor/echarts/echarts.min.js"></script>
3 基础网络服务
3.1 代码参考及解析
先参考一段代码
app. _init _.py
...
def create_app(config_name):
app = Flask(__name__)
app.config.from_object(config[config_name])
config[config_name].init_app(app)
# 为app增加功能
db.init_app(app)
login_manager.init_app(app)
mail.init_app(app)
bootstrap.init_app(app)
moment.init_app(app)
# 注册蓝本
# 主函数
from .main import main as main_blueprint
app.register_blueprint(main_blueprint)
...
project.config.py
# 数据库/Mail两个是刚性设置
...
class Config:
SECRET_KEY = '加密的种子文本,最好从环境变量读入'
# 数据库
SQLALCHEMY_COMMIT_ON_TEARDOWN = True
# 类方法
# Config.init_app('') 直接调用
@staticmethod
def init_app(app):
print('*** creating app by Config class')
...
project.manage.py
...
app = create_app('test')
...
manager = Manager(app)
if __name__ == '__main__':
# 阻塞版
manager.run()
启动的结构为,进入project目录,执行命令(调试模式)。
python3 manager.py runserver -d
- 1 manage.py 包装了app再启动的
- 2 app是通过init.py里面的create_app创建的
- 3 init.py初始文件从config.py里读取了配置。因为此时启动的路径是config.py的同级目录,所以在init.py中是这样导入配置的
- 4 在init.py文件中,子模块通过蓝图的方式挂到了app下面。
from config import config
Note:如果把app当成一个程序包就比较好理解了。比较特殊的是static和template两个文件夹,被flask固定使用了。
原来的flask服务里面加了很多组件,但是作为基础板块,我们都不需要加,只要留一个框子就可以了。
3.2 构建最精简的web结构
最终的项目结构如下
base_web
├── Dockerfile
├── app
│ ├── __init__.py
│ ├── main
│ │ ├── __init__.py
│ │ └── views.py
│ ├── static
│ │ ├── img
│ │ └── vendor
│ └── templates
│ ├── base.html
│ └── main
├── config
│ └── config.py
├── data
├── entry_base_web.py
├── env
│ ├── entrypoint.sh
│ └── requirements.txt
├── log
└── packages
└── DataManipulation-0.1.16.10-py3-none-any.whl
我们按顺序进行说明(项目的依赖包,环境什么的就不说了)
- 1 首先我们得有一个入口函数
entry_base_web.py
函数很简单,就是调用app(此时被视为一个函数包,因为有init文件,python会自动认定)中的create_app函数,生成实际运行的app,并且设置为调试模式。
from app import create_app
if __name__ == '__main__':
app = create_app('test')
app.run(debug=True)
- 2 我们得要在app下面的
__init__.py
文件中创建create_app函数(当然另起一个文件也行)
from flask import Flask
# >>>>>>>>>>>>>>>>>>> 为了给Jinja导入bootstrap/wtf_form.html
from flask_bootstrap import Bootstrap
bootstrap = Bootstrap()
# >>>>>>>>>>>>>>>>>>> 自定义通用函数包
import DataManipulation as dm
# >>>>>>>>>>>>>>>>>>> 创建app的函数,可以用于以后灵活的加插件
def create_app(config_name):
app = Flask(__name__)
bootstrap.init_app(app)
# 注册蓝本
# 1 默认一个主函数
from .main import main as main_blueprint
app.register_blueprint(main_blueprint)
return app
# >>>>>>>>>>>>>>>>>>> 值错误的处理,暂时不定义
class ValidationError(ValueError):
pass
在app包的初始化文件中,其实只是导入了Flask基本包,Bootstrap插件,以及我自定义的DataManipulation包。在create_app中我们引入了一个main主函数,避免网站起来什么都看不到。
- 3 创建main基本包。写入对应的
__init__.py
和views.py
两个文件。前者负责沟通结构,把定义的视图函数传给main包,以便create_app通过蓝图注册。__init__.py
from flask import Blueprint
# 优先指定蓝本
# main = Blueprint('main', __name__, url_prefix='/main')
# main的目录就是根目录
main = Blueprint('main', __name__)
from . import views
views.py
# 导入蓝图名称
from . import main
# 导入最基本的必要功能
from flask import render_template, jsonify
import DataManipulation as dm
# 定义whoami
@main.route('/whoami/', methods=['GET', 'POST'])
def whoami():
res_dict ={}
res_dict['hostname'] = dm.get_machine_hostname()
res_dict['machine_ip'] = dm.get_machine_ip()
res_dict['eid'] ='No Claimed'
return jsonify(res_dict)
# 定义基础页面
@main.route('/', methods=['GET', 'POST'])
def index():
return render_template('/main/index.html')
一个是whoami函数,指明了本机的身份。会以接口形式返回结果。
另一个则是网站启动时显示的函数。里面用到了一个页面index.html
{% extends 'base.html'%}
{%block navcontent%}
<div class="container-fluid"><a xtype="a" class="navbar-brand" href="#">Base_Web</a>
<ul xtype="h5_ele" class="navbar-nav navbar-top mr-5">
<li xtype="h5_ele" class="nav-item"><a xtype="h5_ele" href="{{url_for('main.index')}}" class="nav-link">主页</a>
</li>
</ul>
</div>
{%endblock%}
{%block pagecontent%}
<!-- 空白行作为间隔 -->
<div class="m-5"></div>
<div class="row">
<div class="col md-12">
<div xtype="h5_ele" class="jumbotron">
<h1>BaseWeb</h1>
<hr>
<p> BaseWeb</p>
<p> BaseWeb</p>
<p> BaseWebBaseWebBaseWebBaseWebBaseWebBaseWeb</p>
<p> BaseWebBaseWebBaseWebBaseWebBaseWebBaseWeb</p>
<p> BaseWebBaseWebBaseWebBaseWebBaseWebBaseWeb</p>
</div>
</div>
</div>
<!-- 空白行作为间隔 -->
<div class="m-5"></div>
<div class="row">
<div class="col md-12">
<img src="static/img/main/test1.jpg" alt="" style="width: 100%;">
</div>
</div>
{%endblock%}
页面继承了基础的页面 base.html
并随便写了些内容。
<!DOCTYPE html>
<html>
{% import "bootstrap/wtf.html" as wtf %}
{%block head%}
<!-- 1 网页图标 -->
<link rel="shortcut icon" href="/static/img/tem_logo.ico" type="image/x-icon">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- 2 Bootstrap4 -->
<link rel="stylesheet" href="/static/vendor/Bootstrap/css/bootstrap.min.css">
<script src="/static/vendor/jquery/jquery.js"></script>
<script src="/static/vendor/Bootstrap/js/bootstrap.min.js"></script>
<script src="/static/vendor/Bootstrap/js/popper.min.js"></script>
<!-- 3 DataTables -->
<link rel="stylesheet" href="/static/vendor/DataTables/css/dataTable.css">
<script src="/static/vendor/DataTables/js/dataTable.js"></script>
<!-- 3.1下载数据表的插件 -->
<script src="/static/vendor/DataTables/js/dataTables.buttons.min.js"></script>
<script src="/static/vendor/DataTables/js/buttons.html5.min.js"></script>
<!-- 3.2下载数据表的插件 -->
<script src="/static/vendor/ajax/pdfmake.min.js"></script>
<!-- vfs_fonts要在pdfmake之后导入,否则会找不到字体 -->
<script src="/static/vendor/ajax/vfs_fonts.js"></script>
<script src="/static/vendor/ajax/jszip.min.js"></script>
<!-- 4 inputfile -->
<link href="/static/vendor/fileinput/css/fileinput.min.css" media="all" rel="stylesheet" type="text/css" />
<!-- FontAweson 要放在inputfile之后 -->
<link rel="stylesheet" href="/static/vendor/FontAwesome/css/font-awesome.min.css">
<link href="/static/vendor/fileinput/themes/explorer-fa/theme.css" media="all" rel="stylesheet" type="text/css" />
<script src="/static/vendor/fileinput/js/plugins/sortable.js" type="text/javascript"></script>
<script src="/static/vendor/fileinput/js/fileinput.min.js" type="text/javascript"></script>
<script src="/static/vendor/fileinput/js/locales/zh.js" type="text/javascript"></script>
<script src="/static/vendor/fileinput/themes/explorer-fa/theme.js" type="text/javascript"></script>
<script src="/static/vendor/fileinput/themes/fa/theme.js"></script>
<!-- 5 echarts-->
<script src="/static/vendor/echarts/echarts.min.js"></script>
<title>BaseWeb</title>
{%endblock%}
{%block nav%}
<nav class="navbar navbar-expand-md bg-dark navbar-dark">
{%block navcontent%}
<!-- Brand -->
<a class="navbar-brand" href="{{url_for('main.index')}}">BaseWeb</a>
{%endblock%}
</nav>
{%endblock%}
{%block body%}
<body>
<div id="a0"></div>
<div class="container">
{%for message in get_flashed_messages()%}
<div class='alert alert-warning'>
<button type='button' class='close' data-dismiss='alert'> ×</button>
{{message}}
</div>
{%endfor%}
{%block pagecontent%}
<!-- row1 -->
<div class="row">
<div class="col">
First Row First Col
</div>
</div>
{%endblock%}
</div>
</body>
{%endblock%}
</html>
base.html导入了网站所需的资源。到这里就完成了最简网站的所有配置,可以启动了。切换到项目所在的文件夹,执行:
python3 entry_base_web.py
在浏览其中访问,可以看到后端在第一次访问时载入了资源
前端的效果
后面就可以在这个基础上进行各种修改了。