前言

最近用 python tordado 框架, 整了一个模板页面, 用于接入与发布数据的展示,

tornado 简单易用, bootstrap 比较流行, 用起来也省事, 配合起来做些小案例非常迅速.

技术储备

  1. python

    基础知识, 面向对象封装,继承

  2. 数据库

    mysql

  3. 框架

    tornado, sqlalchemy (ORM), template

  4. 开发工具

    pycharm, chrome

功能开发

一. 需求分析

  1. 每页展示 5 条统计数据, 按日期倒序排列

    没有统计数据时, 要有文字提示: "暂没有对接统计信息"

  2. 支持按日期进行查询

    查询时不显示分页情况

  3. 支持分页查询, 提示目前所在页码, 可跳转到任意一页

二. 页面原型

个人前端水平有限, 弄这样一个页面从无到有估计得花半天时间, 所以就找了 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">&laquo;</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">&raquo;</span>
</a>
</li>
<span style="font-size: 25px;margin-left: 5px;">
共{{ total_count }}条记录,共{{ total_page }}页
</span>
</ul>
</nav>
</div>
{% end %}
</div>
</body>
</html>

需要注意的地方

  1. 模板渲染的时候, 需要使用 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>

  1. 给当前页添加激活状态

项目改进

xxx

最后给出web项目地址: https://github.com/kaichenkai/TornadoWebApp

ending ~

Tornado + Bootstrap 快速搭建自己的web应用的更多相关文章

  1. 基于ASP.NET Core 3.0快速搭建Razor Pages Web应用

    前言 虽然说学习新的开发框架是一项巨大的投资,但是作为一个开发人员,不断学习新的技术并快速上手是我们应该掌握的技能,甚至是一个.NET Framework开发人员,学习.NET Core 新框架可以更 ...

  2. 利用Bootstrap快速搭建个人响应式主页(附演示+源码)

    1.前言 我们每个程序员都渴望搭建自己的技术博客平台与他人进行交流分享,但使用别人的博客模板没有创意.做网站后台的开发人员可能了解前端,可是自己写一个不错的前端还是很费事的.幸好我们有Bootstra ...

  3. bootstrap快速搭建属于自己的后台模板库

    不论做什么项目,我们都以快速搭建为主,设计师固然重要,但是,我们前端开发的也必须能给出自己以前做过什么样的模板,自己收藏的模板,或者我们弹框的形式,我的提示框的形式,我用的下拉框的插件,日历的插件,我 ...

  4. Laravel-Easy-Admin 快速搭建数据后台 web管理后台

    基于PHP + Laravel + element-admin-ui 搭建的快速数据后台,只在解决系列后台增删改查等日常操作.快速搭建,在生成业务的同时可以花更多的时间关注技术本身,提高程序员自身进阶 ...

  5. 前端框架Bootstrap - 快速搭建网站

    Bootstrap简介         Bootstrap是Twitter推出的一个开源的用于前端开发的工具包.是一个CSS/HTML/JavaScript框架.Bootstrap是基于HTML5和C ...

  6. tornado+bootstrap急速搭建你自己的网站

    bootstrap既然是这么的流行又能省很多的事为什么不用他呢?再加上牛X的produced by FB的tornado简直如虎添翼了! 1. 安装配置 安装所需要的库等内容.这里没什么需要多讲的.t ...

  7. 快速搭建Web环境 Angularjs + Express3 + Bootstrap3

    快速搭建Web环境 Angularjs + Express3 + Bootstrap3 AngularJS体验式编程系列文章, 将介绍如何用angularjs构建一个强大的web前端系统.angula ...

  8. windows下如何快速搭建web.py开发框架

    在windows下如何快速搭建web.py开发框架 用Python进行web开发的话有很多框架供选择,比如最出名的Django,tornado等,除了这些框架之外,有一个轻量级的框架使用起来也是非常方 ...

  9. 在windows下如何快速搭建web.py开发框架

    在windows下如何快速搭建web.py开发框架 用Python进行web开发的话有很多框架供选择,比如最出名的Django,tornado等,除了这些框架之外,有一个轻量级的框架使用起来也是非常方 ...

随机推荐

  1. mysql/mongodb监控之Percona Monitoring and Management (PMM) 2.1.0安装使用

    Percona Monitoring and Management (PMM)是Percona Server一款开源的用于管理和监控MySQL和MongoDB性能的开源平台,通过PMM客户端收集到的D ...

  2. supervisor配置进程

    具体配置见此页面:https://www.jianshu.com/p/ac6c84a2f415 设置开机启动:https://blog.csdn.net/xyang81/article/details ...

  3. Django入门3 简单留言板项目案例及mysql驱动的安装配置

    新建jangostart项目 使用manager.py新建app即单独的应用 创建一个message应用 manage.py@djangostart > startapp message 如果a ...

  4. k8s记录-Dockerfile详解

    Dockerfile命令详解 下面我们来分别介绍下上面使用到的命令: FROM 格式:FROM 或FROM :. 解释:FROM必须是Dockerfile里的第一条指令(注视除外),后面跟有效的镜像名 ...

  5. Spark原理分析目录

    1 Spark原理分析 -- RDD的Partitioner原理分析 2 Spark原理分析 -- RDD的shuffle简介 3 Spark原理分析 -- RDD的shuffle框架的实现概要分析 ...

  6. WeQuant教程—1.3 利用回测工具降低交易风险

    量化系统投入实际使用之前,人们会希望提前测试交易的效果.这个期间往往涉及代码的改动和参数的调整.最常见的做法是将历史数据输入量化系统,让量化系统根据既定的交易逻辑进行操作,观察和分析交易结果,找到问题 ...

  7. 【python基础】easydict的安装与使用

    前言 easydict允许以属性的方式访问dict类型,且可以递归地访问,使用起来比较方便. 安装 pip install easydict 使用 参考 1. easydict; 完

  8. lnmp+discuz使用redis缓存(待进一步研究)

    一直说Redis.Redis缓存.一直不清楚怎么用.于是花点时间研究了一下,但是还没搞懂.先把大概内容记录一下,待后续继续学习 1.首先部署lnmp环境,这个我的博客有些,请自行搜索 2.给php添加 ...

  9. go中值传递、引用传递、指针传递的区别

    go语言中的值类型: int.float.bool.array.sturct等 值传递是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数 声明一个值类 ...

  10. SonarQube - 常用配置与操作

    1 - SonarQube服务器中的数据库配置 2019年4月10号,SonarQube发文称在7.9之后,所有的SonarQube的版本(CE.DE.EE和DCE)中将停止对MySQL的支持. 建议 ...