Tornado + Bootstrap 快速搭建自己的web应用
前言
最近用 python tordado 框架, 整了一个模板页面, 用于接入与发布数据的展示,
tornado 简单易用, bootstrap 比较流行, 用起来也省事, 配合起来做些小案例非常迅速.
技术储备
python
基础知识, 面向对象封装,继承
数据库
mysql
框架
tornado, sqlalchemy (ORM), template
开发工具
pycharm, chrome
功能开发
一. 需求分析
每页展示 5 条统计数据, 按日期倒序排列
没有统计数据时, 要有文字提示: "暂没有对接统计信息"
支持按日期进行查询
查询时不显示分页情况
支持分页查询, 提示目前所在页码, 可跳转到任意一页
二. 页面原型
个人前端水平有限, 弄这样一个页面从无到有估计得花半天时间, 所以就找了 bootstrap 的模板, 里边有很多现成的组件可以直接使用, 然后改下布局, 调下样式, 做一个简单的页面足够.
bootstrap : https://v3.bootcss.com/components/

三. 搭建 tornado 框架
熟悉 python 的同学, 直接 easy_install 或者 pip 安装最新的 tornado, sqlalchemy, pymysql 等模块
bootstrap.js 直接到官网下载就行, 当然还需要 bootstrap 依赖的 JQuery.js
app.py
配置静态资源路径:
"static_path": os.path.join(os.path.dirname(__file__), "statics"),
配置模板路径:
"template_path": os.path.join(os.path.dirname(__file__), 'templates'),
配置运行模式:
# 通过脚本传参的方式指定
define("debug", default=1, help="debug mode: 1 to open, 2 to test env, other to production")
"debug": bool(options.debug),
开发, 调试模式, 开启单进程:
application.listen(options.port)
tornado.ioloop.IOLoop.instance().start()
生产模式, 开启多进程:
application.bind(options.port)
application.start(3) # 开启 3 个进程
tornado.ioloop.IOLoop.instance().start()
四. 连接 mysql
获取了DBSession 类, 在需要使用的地方, 创建对象[ session = DBSession() ]即可,
如果数据库操作不是特别平凡, 会话回收的时间周期可以设置的长一点, 例如: pool_recycle = 60
# coding: utf-8
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from sqlalchemy import create_engine
from settings.setting import MYSQL_SERVER, MYSQL_DRIVER, MYSQL_USERNAME, MYSQL_PASSWORD, DB_NAME, DB_CHARSET
# MYSQL_USERNAME = os.getenv('MYSQL_USER') or settings.MYSQL_USERNAME
# MYSQL_PASSWORD =
# 数据库
engine = create_engine("mysql+{driver}://{username}:{password}@{server}/{database}?charset={charset}"
.format(driver=MYSQL_DRIVER,
username=MYSQL_USERNAME,
password=MYSQL_PASSWORD,
server=MYSQL_SERVER,
database=DB_NAME,
charset=DB_CHARSET),
pool_size=20,
max_overflow=100,
pool_recycle=1,
echo=False)
engine.execute("SET NAMES {charset};".format(charset=DB_CHARSET))
MapBase = declarative_base(bind=engine)
DBSession = sessionmaker(bind=engine)
五. 处理类
基础类, 定义了一些 API 规范和常用的工具方法
先封装了基础类 GlobalBaseHandler , 数据库会话和获取参数的方法封装
# 全局基类方法
class GlobalBaseHandler(BaseHandler):
@property
def session(self):
if hasattr(self, "_session"):
return self._session
self._session = DBSession()
return self._session
# 关闭数据库会话
def on_finish(self):
if hasattr(self, "_session"):
self._session.close()
def prepare(self):
pass
查询, 分页业务处理类
需要注意的是: (小于第1页情况); (大于总页数的情况, 但总页数为 0 的情况).
class IllegalStats(GlobalBaseHandler):
def get(self):
"""
获取接入,发布统计数据
:return:
"""
date = self.get_argument("date")
current_page = self.get_argument("current_page") # 当前页
if date:
return self.query_by_date(date)
elif current_page:
return self.query_paginate(current_page)
# 默认返回第一页的数据
else:
return self.query_paginate()
def query_paginate(self, current_page=None):
if current_page is None:
current_page = 1
else:
current_page = int(current_page)
#
page_size = 5 # 分页条数
# 总记录数, 总页数
total_count = IllegalAccessStats.query_count(self.session)
if total_count % page_size == 0:
total_page = int(total_count / page_size)
else:
total_page = int(total_count / page_size) + 1
# 小于第一页
if current_page <= 0:
current_page = 1
# 大于最后一页
if total_page > 0:
if current_page > total_page:
current_page = total_page
else:
current_page = 1
# 当前页的数据
stats_list = IllegalAccessStats.query_paginate(self.session, current_page=current_page, page_size=page_size)
return self.render("statistics.html", stats_list=stats_list, date="", total_count=total_count, total_page=total_page, current_page=current_page)
def query_by_date(self, date):
stats_list = IllegalAccessStats.query_by_date(self.session, date=date)
#
return self.render("statistics.html", stats_list=stats_list, date=date, total_count=None, total_page=None, current_page=None)
六. 模板渲染
template, 就是在 html 里边写 python 代码, if 判断, for range 语句和python语法一模一样, 没有太大难度, 慢慢调试就出来了, 模板渲染报错提示非常详细, 很好调试,
感觉写起来贼恶心, 又是标签, 又是逻辑处理.
statistics.html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<!-- viewport视口:网页可以根据设置的宽度自动进行适配,在浏览器的内部虚拟一个容器,容器的宽度与设备的宽度相同。
width: 默认宽度与设备的宽度相同
initial-scale: 初始的缩放比,为1:1 -->
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>接入&发布统计信息</title>
<!-- Bootstrap -->
<link href="{{static_url('css/bootstrap.min.css')}}" rel="stylesheet">
<script src="{{static_url('js/jquery-2.1.0.min.js')}}"></script>
<script src="{{static_url('js/bootstrap.min.js')}}"></script>
<style type="text/css">
th, td {
text-align: center;
height: 50px;
vertical-align:middle;
}
</style>
</head>
<body>
<div class="container">
<h2 style="text-align: center">接入 & 发布统计信息</h2>
<br>
<br>
<div style="float: left;">
<form class="form-inline" action="/illegal/stats" method="get">
<div class="form-group">
<label for="exampleInputName1">日期</label>
<input type="text" name="date" value="{{ date }}" class="form-control" id="exampleInputName1" placeholder="yyyy-mm-dd" >
</div>
<button type="submit" class="btn btn-default" style="margin: 5px;">查询</button>
</form>
</div>
<br>
<br>
<br>
<br>
<br>
<table border="1" class="table table-bordered table-hover">
<div>
<a class="btn btn-primary" href="/illegal/stats?" style="float: left; margin: 3px;">接入</a>
<a class="btn btn-primary" href="/illegal/stats?" style="position: relative; left: 872px; margin: 3px">发布</a>
</div>
<tr class="access">
<th style="background: #f8efc0; vertical-align:middle">日期</th>
<th style="background: #4cae4c; vertical-align:middle">违法接收</th>
<th style="background: #4cae4c; vertical-align:middle">入库成功</th>
<th style="vertical-align: middle">读取成功</th>
<th style="background: #d9534f; vertical-align:middle">读取失败</th>
<th style="vertical-align: middle">下载成功</th>
<th style="background: #d9534f; vertical-align:middle">下载失败</th>
<th style="vertical-align: middle">写入成功</th>
<th style="background: #d9534f; vertical-align:middle">写入失败</th>
<th style="background: #4cae4c; vertical-align:middle">审核接收</th>
<th style="background: #4cae4c; vertical-align:middle">发布成功</th>
</tr>
{% if len(stats_list) > 0 %}
{% for stats in stats_list %}
<tr>
<td style="vertical-align: middle">{{ stats["date"] }}</td>
<td style="vertical-align: middle">{{ stats["access_received_total"] }}</td>
<td style="vertical-align: middle">{{ stats["access_inserted_total"] }}</td>
<td style="vertical-align: middle">{{ stats["read_success_total"] }}</td>
<td style="vertical-align: middle">{{ stats["read_false_total"] }}</td>
<td style="vertical-align: middle">{{ stats["download_success_total"] }}</td>
<td style="vertical-align: middle">{{ stats["download_false_total"] }}</td>
<td style="vertical-align: middle">{{ stats["write_success_total"] }}</td>
<td style="vertical-align: middle">{{ stats["write_false_total"] }}</td>
<td style="vertical-align: middle">{{ stats["publish_received_total"] }}</td>
<td style="vertical-align: middle">{{ stats["publish_send_total"] }}</td>
</tr>
{% end %}
{% else %}
<tr>
<td colspan="11" style="vertical-align: middle">暂没有对接统计信息</td>
</tr>
{% end %}
</table>
<!-- 查询的时候不显示分页内容-->
{% if total_count is not None or total_page is not None %}
<div>
<nav aria-label="Page navigation">
<ul class="pagination">
{% if current_page == 1 %}
<li class="disabled">
{% end %}
{% if current_page != 1 %}
<li>
{% end %}
<a href="/illegal/stats?current_page={{current_page - 1}}" aria-label="Previous">
<span aria-hidden="false">«</span>
</a>
</li>
{% for page in range(1, total_page + 1) %}
{% if current_page == page %}
<li class="active"><a href="/illegal/stats?current_page={{ page }}">{{ page }}</a></li>
{% end %}
{% if current_page != page %}
<li><a href="/illegal/stats?current_page={{ page }}">{{ page }}</a></li>
{% end %}
{% end %}
{% if current_page == total_page or current_page == 1 %}
<li class="disabled">
{% end %}
{% if current_page != total_page and current_page != 1 %}
<li>
{% end %}
<a href="/illegal/stats?current_page={{current_page + 1}}" aria-label="Next">
<span aria-hidden="true">»</span>
</a>
</li>
<span style="font-size: 25px;margin-left: 5px;">
共{{ total_count }}条记录,共{{ total_page }}页
</span>
</ul>
</nav>
</div>
{% end %}
</div>
</body>
</html>
需要注意的地方
模板渲染的时候, 需要使用
static_url()函数获取静态资源( statics ) 的路径, 不然模板会找不到 css, js 文件, 不能正常加载页面.<link href="{{static_url('css/bootstrap.min.css')}}" rel="stylesheet"><script src="{{static_url('js/jquery-2.1.0.min.js')}}"></script><script src="{{static_url('js/bootstrap.min.js')}}"></script>
给当前页添加激活状态

项目改进
xxx
最后给出web项目地址: https://github.com/kaichenkai/TornadoWebApp
ending ~
Tornado + Bootstrap 快速搭建自己的web应用的更多相关文章
- 基于ASP.NET Core 3.0快速搭建Razor Pages Web应用
前言 虽然说学习新的开发框架是一项巨大的投资,但是作为一个开发人员,不断学习新的技术并快速上手是我们应该掌握的技能,甚至是一个.NET Framework开发人员,学习.NET Core 新框架可以更 ...
- 利用Bootstrap快速搭建个人响应式主页(附演示+源码)
1.前言 我们每个程序员都渴望搭建自己的技术博客平台与他人进行交流分享,但使用别人的博客模板没有创意.做网站后台的开发人员可能了解前端,可是自己写一个不错的前端还是很费事的.幸好我们有Bootstra ...
- bootstrap快速搭建属于自己的后台模板库
不论做什么项目,我们都以快速搭建为主,设计师固然重要,但是,我们前端开发的也必须能给出自己以前做过什么样的模板,自己收藏的模板,或者我们弹框的形式,我的提示框的形式,我用的下拉框的插件,日历的插件,我 ...
- Laravel-Easy-Admin 快速搭建数据后台 web管理后台
基于PHP + Laravel + element-admin-ui 搭建的快速数据后台,只在解决系列后台增删改查等日常操作.快速搭建,在生成业务的同时可以花更多的时间关注技术本身,提高程序员自身进阶 ...
- 前端框架Bootstrap - 快速搭建网站
Bootstrap简介 Bootstrap是Twitter推出的一个开源的用于前端开发的工具包.是一个CSS/HTML/JavaScript框架.Bootstrap是基于HTML5和C ...
- tornado+bootstrap急速搭建你自己的网站
bootstrap既然是这么的流行又能省很多的事为什么不用他呢?再加上牛X的produced by FB的tornado简直如虎添翼了! 1. 安装配置 安装所需要的库等内容.这里没什么需要多讲的.t ...
- 快速搭建Web环境 Angularjs + Express3 + Bootstrap3
快速搭建Web环境 Angularjs + Express3 + Bootstrap3 AngularJS体验式编程系列文章, 将介绍如何用angularjs构建一个强大的web前端系统.angula ...
- windows下如何快速搭建web.py开发框架
在windows下如何快速搭建web.py开发框架 用Python进行web开发的话有很多框架供选择,比如最出名的Django,tornado等,除了这些框架之外,有一个轻量级的框架使用起来也是非常方 ...
- 在windows下如何快速搭建web.py开发框架
在windows下如何快速搭建web.py开发框架 用Python进行web开发的话有很多框架供选择,比如最出名的Django,tornado等,除了这些框架之外,有一个轻量级的框架使用起来也是非常方 ...
随机推荐
- [技术博客] 利用Vagrant+virtualbox在windows下进行linux开发
目录 加速box安装的方法 root账户登录 换源教程 安装rvm 访问rails server RubyMine连接虚拟机上的解释器 作者:庄廓然 在windows下进行linux开发:利用Vagr ...
- 用Python画一颗特别的心送给她
import numpy as np import matplotlib.pyplot as plt x_coords = np.linspace(-100, 100, 500) y_coords = ...
- Transform the vot dataset into 4 corner format
Transform the vot dataset into 4 corner format Matlab code to change the 8 value ground truth into 4 ...
- QPS/TPS的预估
先说标准概念: TPS:Transactions Per Second(每秒传输的事物处理个数),即服务器每秒处理的事务数.TPS包括一条消息入和一条消息出,加上一次用户数据库访问.(业务TPS = ...
- Windows Server 2012 R2 卸载IE浏览器
If you run any Windows Servers, you may run into a scenario where you want to remove access to Inter ...
- zookeeper生产最广泛使用java客户端curator介绍及其它客户端比较
关于zookeeper的原理解析,可以参见zookeeper核心原理详解,本文所述大多数实践基于对zookeeper原理的首先理解. Curator是Netflix公司开源的一个Zookeeper客户 ...
- kotlin基础 函数编写规则
- MQTT研究之EMQ:【CoAP协议应用开发】
本博文的重点是尝试CoAP协议的应用开发,其中包含CoAP协议中一个重要的开源工具libcoap的安装和遇到的问题调研.当然,为了很好的将EMQ的CoAP协议网关用起来,也调研了下EMQ体系下,CoA ...
- Linux 磁盘管理_016
以5个方面讲解 1. 硬盘 2. 磁盘RAID.LVM等 3. 磁盘分区 4. 磁盘格式化 5. 磁盘挂载后磁盘管理 一.硬盘 硬盘分类 备注 机械硬盘 IDE SCSI SATA SAS 固态 ...
- vbscript--FileSystemObject详解
https://blog.csdn.net/superbirds/article/details/6762748 FSO是FileSystemObject 或 Scripting.FileSystem ...