Jenkins之权限分配

一、操作环境

1、Jenkins

  • Jenkins 2.75

二、权限配置地点

在Jenkins的主界面中,点击【系统管理】

未分类

在【系统管理】中点击【Configure Global Security】

未分类

在【Configure Global Security】页面中,在“授权策略”中将“项目矩阵授权策略”选中

未分类

三、配置权限

在配置权限前,首先得将自己的系统用户创建好,一个是拥有所有权限的系统管理员admin,另一个是专供开发人员所使用的dev,创建好之后如下:

未分类

在刚才的“授权策略”中,在“添加用户/组”中,添加“admin”点击【添加】按钮,之后重复刚才的操作,添加“dev”

未分类

由于“admin”是系统管理员,所以说拥有全部的权限,所以说将全部的权限复选框全部选中,而dev是属于开发人员,因而我们只希望其具有项目构建的权限,所以说我们会只给其分配“Read”和“Build”权限,而匿名用户,我们往往只希望其能够查看项目,不允许其进行任何的操作,因而只分配“Read”权限,分配完之后的结果如下所示:

未分类

Centos7下Etcd集群搭建

一、简介

“A highly-available key value store for shared configuration and service discovery.”

Etcd是coreos开发的分布式服务系统,内部采用raft协议作为一致性算法。作为一个高可用的配置共享、服务发现的键值存储系统,Etcd有以下的特点:

  • 简单:安装配置简单,而且提供了 HTTP API 进行交互,使用也很简单
  • 安全:支持 SSL 证书验证
  • 快速:根据官方提供的数据,单实例支持每秒2k+读操作、1k写操作
  • 可靠:采用raft算法,实现分布式系统数据的可用性和一致性

Etcd构建自身高可用集群主要有三种形式:

  • 静态发现: 预先已知 Etcd 集群中有哪些节点,在启动时直接指定好Etcd的各个node节点地址
  • Etcd动态发现: 通过已有的Etcd集群作为数据交互点,然后在扩展新的集群时实现通过已有集群进行服务发现的机制
  • DNS动态发现: 通过DNS查询方式获取其他节点地址信息

本文主要介绍第一种方式,后续会陆续介绍剩下的两种方式。(直接docker安装请移步:quay.io/coreos/etcd 基于Docker镜像的集群搭建)

二、环境介绍

三台虚拟机,系统环境均为Centos7,对应节点名称及IP地址如下:

  • node1:192.168.7.163
  • node2:192.168.7.57
  • etcd2:192.168.7.58

首先将这个信息添加到三台主机的hosts文件中,编辑/etc/hosts,填入以下信息:

  • 192.168.7.163 node1
  • 192.168.7.57 node2
  • 192.168.7.58 etcd2

三、安装、配置Etcd

# yum install etcd -y

yum安装的etcd默认配置文件在/etc/etcd/etcd.conf,以下将三个节点上的配置贴出来,请注意不同点(未贴出的,则表明不需要更改)

node1

# [member]
# 节点名称
ETCD_NAME=node1
# 数据存放位置
ETCD_DATA_DIR="/var/lib/etcd/default.etcd"
#ETCD_WAL_DIR=""
#ETCD_SNAPSHOT_COUNT="10000"
#ETCD_HEARTBEAT_INTERVAL="100"
#ETCD_ELECTION_TIMEOUT="1000"
# 监听其他 Etcd 实例的地址
ETCD_LISTEN_PEER_URLS="http://0.0.0.0:2380"
# 监听客户端地址
ETCD_LISTEN_CLIENT_URLS="http://0.0.0.0:2379,http://0.0.0.0:4001"
#ETCD_MAX_SNAPSHOTS="5"
#ETCD_MAX_WALS="5"
#ETCD_CORS=""
#
#[cluster]
# 通知其他 Etcd 实例地址
ETCD_INITIAL_ADVERTISE_PEER_URLS="http://node1:2380"
# if you use different ETCD_NAME (e.g. test), set ETCD_INITIAL_CLUSTER value for this name, i.e. "test=http://..."
# 初始化集群内节点地址
ETCD_INITIAL_CLUSTER="node1=http://node1:2380,node2=http://node2:2380,etcd2=http://etcd2:2380"
# 初始化集群状态,new 表示新建
ETCD_INITIAL_CLUSTER_STATE="new"
# 初始化集群 token
ETCD_INITIAL_CLUSTER_TOKEN="mritd-etcd-cluster"
# 通知 客户端地址
ETCD_ADVERTISE_CLIENT_URLS="http://node1:2379,http://node1:4001"

node2

# [member]
ETCD_NAME=node2
ETCD_DATA_DIR="/var/lib/etcd/default.etcd"
#ETCD_WAL_DIR=""
#ETCD_SNAPSHOT_COUNT="10000"
#ETCD_HEARTBEAT_INTERVAL="100"
#ETCD_ELECTION_TIMEOUT="1000"
ETCD_LISTEN_PEER_URLS="http://0.0.0.0:2380"
ETCD_LISTEN_CLIENT_URLS="http://0.0.0.0:2379,http://0.0.0.0:4001"
#ETCD_MAX_SNAPSHOTS="5"
#ETCD_MAX_WALS="5"
#ETCD_CORS=""
#
#[cluster]

ETCD_INITIAL_ADVERTISE_PEER_URLS="http://node2:2380"
# if you use different ETCD_NAME (e.g. test), set ETCD_INITIAL_CLUSTER value for this name, i.e. "test=http://..."
ETCD_INITIAL_CLUSTER="node1=http://node1:2380,node2=http://node2:2380,etcd2=http://etcd2:2380"
ETCD_INITIAL_CLUSTER_STATE="new"
ETCD_INITIAL_CLUSTER_TOKEN="mritd-etcd-cluster"
ETCD_ADVERTISE_CLIENT_URLS="http://node2:2379,http://node2:4001"

etcd2

# [member]
ETCD_NAME=etcd2
ETCD_DATA_DIR="/var/lib/etcd/default.etcd"
#ETCD_WAL_DIR=""
#ETCD_SNAPSHOT_COUNT="10000"
#ETCD_HEARTBEAT_INTERVAL="100"
#ETCD_ELECTION_TIMEOUT="1000"
ETCD_LISTEN_PEER_URLS="http://0.0.0.0:2380"
ETCD_LISTEN_CLIENT_URLS="http://0.0.0.0:2379,http://0.0.0.0:4001"
#ETCD_MAX_SNAPSHOTS="5"
#ETCD_MAX_WALS="5"
#ETCD_CORS=""
#
#[cluster]
ETCD_INITIAL_ADVERTISE_PEER_URLS="http://etcd2:2380"
# if you use different ETCD_NAME (e.g. test), set ETCD_INITIAL_CLUSTER value for this name, i.e. "test=http://..."
ETCD_INITIAL_CLUSTER="node1=http://node1:2380,node2=http://node2:2380,etcd2=http://etcd2:2380"
ETCD_INITIAL_CLUSTER_STATE="new"
ETCD_INITIAL_CLUSTER_TOKEN="mritd-etcd-cluster"
ETCD_ADVERTISE_CLIENT_URLS="http://etcd2:2379,http://etcd2:4001"

改好配置之后,在各个节点上开启etcd服务:

# systemctl restart etcd

四、测试验证

[root@localhost ~]# etcdctl set testdir/testkey0 0
0
[root@localhost ~]# etcdctl set testdir/testkey1 1
1
[root@localhost ~]# etcdctl set testdir/testkey2 2
2
[root@localhost ~]# etcdctl ls
/test
/testdir
[root@localhost ~]# etcdctl ls testdir
/testdir/testkey0
/testdir/testkey1
/testdir/testkey2
[root@localhost ~]# etcdctl get testdir/testkey2
2
[root@localhost ~]# etcdctl member list
377aa10974e8238d: name=node1 peerURLs=http://node1:2380 clientURLs=http://node1:2379,http://node1:4001 isLeader=true
9de2d4fdbbd835b6: name=etcd2 peerURLs=http://etcd2:2380 clientURLs=http://etcd2:2379,http://etcd2:4001 isLeader=false
f75ed833c7cbbe65: name=node2 peerURLs=http://node2:2380 clientURLs=http://node2:2379,http://node2:4001 isLeader=false

linux下精确指定账号和密码过期时间

以前的一篇文章”linux下忘记用户密码并已过期,且又不能改变密码该怎么办?”(https://shengbao.org/203.html) 中最后,留下过一个彩蛋。如何精确简单的指定账号密码的过期时间?这里其实本意是指定密码过期时间。自己当时问的都不严谨。

未分类

分析问题

我们开始分析chage命令,是否存在指定过期时间的命令。

chage参数意思:

  • -m 密码可更改的最小天数。为零时代表任何时候都可以更改密码。
  • -M 密码保持有效的最大天数。
  • -W 用户密码到期前,提前收到警告信息的天数。
  • -E 帐号到期的日期。过了这天,此帐号将不可用。
  • -d 上一次更改的日期
  • -i 停滞时期。如果一个密码已过期这些天,那么此帐号将不可用。
  • -l 例出当前的设置。由非特权用户来确定他们的密码或帐号何时过期。

首先,我们来看账号信息

未分类

该yunwei账号有效期为永久。我们来使用-E参数指定过期时间。

Chage -E 2017-08-28 yunwei

未分类

从上图 可以看到生效的选项为:accunt expires(账号过期日)。不是我们需要的准确的密码过期选项。

那如何精确的控制密码过期时间呢?

未分类

如上图,我们首先使用chage -M 1 yunwei,密码有效期延后1天。如何精确的控制密码过期时间,只有这个笨办法。

接着我们再来仔细的思考下,如果你有效期设置的时间比较久,是不是要算很大的数字来得出准确的时间呢?

突发奇想,将最后密码修改时间改为今天,是不是更方便呢?

chage -d 2017-08-27 yunwei

然后,想让他月底过期就很方便了

chage -M 4 yunwei

补充

回想了下,chage -d 指定最后密码修改时间。如果将有效期设定为今天,密码有效期会不会自动改变呢?经过测试,密码有效期如果指定为180天的话。如果,chage -d 设置为今天后,密码有效期自动从当前日期开始计算180天。

结论分析

很多知识在学习的时候不仔细,以前一直觉得-M 选项的计数是1970-01-01开始的,后来查看了-E参数,才恍然大悟,这是账号过期时间。不是密码过期时间。密码过期时间=最后修改时间+天数

解决Linux中crontab不执行ntpdate问题

解决Linux中crontab尚未执行ntpdate问题

未分类

[root@localhost ~]# crontab -l
*/1 * * * * ntpdate time.nist.gov

我是每分钟同步时间,上面看上去是没什么问题,但是你能很容易的发现命令行经常弹出You have new mail in /var/spool/mail/root 类似于这种提示。

当你打开这个文件会有如下提示:/bin/sh: ntpdate: command not found

上面的意思是:在/bin/sh 下找不到ntpdate命令

解决办法:

使用whereis命令查找ntpdate命令

[root@localhost ~]# whereis ntpdate
ntpdate: /usr/sbin/ntpdate /usr/share/man/man8/ntpdate.8.gz

然后重新更改crontab定时任务

[root@localhost ~]# crontab -l
*/1 * * * * /usr/sbin/ntpdate time.nist.gov

保存后即可解决。

crontab定时监控tomcat进程 停止时自动启动

一、创建monitor.sh文件

[root@izjjh7u0ge4d6yz tomcat]# vi monitor.sh

编辑内容为:

#!/bin/sh  
#获取tomcat进程ID  
TomcatID=$(ps -ef | grep tomcat | grep -w 'tomcat'|grep -v 'grep'|awk '{print $2}')  
#Tomcat启动程序(根据自己路径编写)  
StartTomcat=/usr/local/tomcat/bin/startup.sh  
TomcatCache=/usr/local/tomcat/work  
#定义要监控的页面地址(写比较简单页面即可)  
WebUrl=https://hao.360.cn/?wd_xp1  
#日志输出  
TomcatMonitorLog=/tmp/TomcatMonitor.log  
Monitor(){  
   echo "[info]开始监控tomcat...[$(date +'%F %H:%M:%S')]"  
   #判断Tomcat进程是否存在  
   if [[ $TomcatID ]];then  
   echo "[info]当前tomcat进程ID为:$TomcatID,继续检测......"  
   #检测是否启动成功(成功的话页面会返回状态"200")  
   TomcatServiceCode=$(curl -I -m 10 -o /dev/null -s -w %{http_code} $WebUrl)  
   if [ $TomcatServiceCode -eq 200 ];then  
     echo "[info]返回码为$TomcatServiceCode,tomcat启动成功,测试正常......"  
  else  
     echo "[error]tomcat出错,请注意......状态码为$TomcatServiceCode,错误日志已输出到$TomcatMonitorLog[error],开始重启tomcat"  
   #杀掉原tomcat进程  
   kill -9 $TomcatID    
  sleep 3  
   #清理tomcat缓存  
  rm -rf $TomcatCache    
  $StartTomcat  
  fi  
 else  
   echo "[error]tomcat进程不存在!tomcat开始自动重启......[info]$StartTomcat,请稍候......"  
  rm -rf $TomcatCache  
  $StartTomcat  
  fi  
 echo "编写完成"  
 }  
 Monitor>>$TomcatMonitorLog  

二、保存退出后,给脚本执行权限

指令:chmod a+xmonitor.sh

三、启动脚本,使用crontab执行定时任务

[root@izjjh7u0ge4d6yztomcat]# ./monitor.sh

看monitor.sh文件是否已经监控,如下图所示:

未分类

使用crontab需要先启动crontab,然后使用vi添加定时执行任务。

[root@izjjh7u0ge4d6yz tomcat]# /sbin/service crond start
[root@izjjh7u0ge4d6yztomcat]# vi /etc/crontab

添加内容为:

*/1 * * * * /usr/local/tomcat/monitor.sh

保存并退出,可以用crontab -l指令来查看当前的定时任务。

定时执行任务Crontab的20个例子

简介

Linux crontab和Windows task schedules非常的相似。Crontab可以用来在系统中定期的执行任务。比如:写了一个爬虫需要每天早上八点执行,就可以用到Crontab;安装的Tomcat服务器需要每天凌晨重启一次,也可以使用到Crontab。总之,几乎所有的定时任务,我们都可以通过Crontab这个工具来完成。

Crontab在Linux上的结构

未分类

从左到右依次为:

[分钟] [小时] [每月的某一天] [每年的某一月] [每周的某一天] [执行的命令]

注意:请留意每个选项的取值范围。

如何 添加/编辑 Crontab

  • 添加或更新crontab中的命令
crontab -e

默认情况下,系统会编辑当前登录用户的crontab命令集合。需要编辑其他用户的命令集合,需要使用到如下的命令

crontab -u username -e

查看Crontab命令集合

  • 查看当前系统登录用户的Crontab命令集合
crontab -l
  • 查看其他用户的Crontab命令集合
crontab -u username -l

20个超实用的Crontab使用实例

  • 每天 02:00 执行任务
0 2 * * * /bin/sh backup.sh
  • 每天 5:00和17:00执行任务
0 5,17 * * * /scripts/script.sh
  • 每分钟执行一次任务

通常情况下,我们并没有每分钟都需要执行的脚本(默默的想到了12306–)

* * * * *  /scripts/script.sh
  • 每周日 17:00 执行任务
0 17 * * sun  /scripts/script.sh
  • 每 10min 执行一次任务
*/10 * * * * /scripts/monitor.sh
  • 在特定的某几个月执行任务
* * * jan,may,aug * /script/script.sh
  • 在特定的某几天执行任务
0 17 * * sun,fri /script/scripy.sh

在每周五、周日的17点执行任务

  • 在某个月的第一个周日执行任务
0 2 * * sun  [ $(date +%d) -le 07 ] && /script/script.sh
  • 每四个小时执行一个任务
0 */4 * * * /scripts/script.sh
  • 每周一、周日执行任务
0 4,17 * * sun,mon /scripts/script.sh
  • 每个30秒执行一次任务

我们没有办法直接通过上诉类似的例子去执行,因为最小的是1min。但是我们可以通过如下的方法。

* * * * * /scripts/script.sh
* * * * *  sleep 30; /scripts/script.sh
  • 多个任务在一条命令中配置
* * * * * /scripts/script.sh; /scripts/scrit2.sh
  • 每年执行一次任务
@yearly /scripts/script.sh

@yearly 类似于“0 0 1 1 *”。它会在每年的第一分钟内执行,通常我们可以用这个发送新年的问候。

  • 每月执行一次任务
@yearly /scripts/script.sh
  • 每周执行一次任务
@yearly /scripts/script.sh
  • 每天执行一次任务
@yearly /scripts/script.sh
  • 每分钟执行一次任务
@yearly /scripts/script.sh
  • 系统重启时执行
@reboot /scripts/script.sh
  • 将 Cron 结果重定向的特定的账户

默认情况下,cron 只会将结果详情发送给 cron 被制定的用户。如果需要发送给其他用户,可以通过如下的方式:

# crontab -l
MAIL=bob
0 2 * * * /script/backup.sh
  • 将所有的 cron 命令备份到文本文件当中

这是一个当我们丢失了cron命令后方便快速的一个恢复方式。
下面是利用这个方式恢复cron的一个小例子。(看看就行~)
首先:检查当前的cron

# crontab -l
MAIL=rahul
0 2 * * * /script/backup.sh

然后:备份cron到文件中

# crontab -l > cron-backup.txt
# cat cron-backup.txt
MAIL=rahul
0 2 * * * /script/backup.sh

接着:移除当前的cron

# crontab -r
# crontab -l
no crontab for root

恢复:从text file中恢复

# crontab cron-backup.txt
# crontab -l
MAIL=rahul
0 2 * * * /script/backup.sh

crontab mysql定时备份

本人环境配置

  • ubuntu: 17.04
  • mysql: 5.7.18-15

编写 备份脚本

$ cat bkDbname.sh
#!/bin/bash
BKPATH="/path/to/shell-path/"
BKDBNAME="dbname"
BKDATE=$(date +%Y%m%d_%H%m%s)
BKFILE=${BKPATH}${BKDBNAME}_${BKDATE}.sql.gz
/usr/bin/mysqldump -uroot -p"password" ${BKDBNAME} | gzip>${BKFILE}

添加定时任务

打开编辑 /etc/crontab, 在末尾添加 定时任务.

 # back database 每天17点备份
 0 17   * * *   willike sh /path/to/shell-path/bkDbname.sh

crontab定时任务 格式
minute hour day month week command
分 时 日 月 周 命令

  • 星号(*):代表所有可能的值,例如month字段如果是星号,则表示在满足其它字段的制约条件后每月都执行该命令操作。
  • 逗号(,):可以用逗号隔开的值指定一个列表范围,例如,“1,2,5,7,8,9”
  • 中杠(-):可以用整数之间的中杠表示一个整数范围,例如“2-6”表示“2,3,4,5,6”
  • 正斜线(/):可以用正斜线指定时间的间隔频率,例如“0-23/2”表示每两小时执行一次。同时正斜线可以和星号一起使用,例如*/10,如果用在minute字段,表示每十分钟执行一次。

python virtualenv 虚拟环境的使用

虚拟环境是一个将不同项目所需求的依赖分别放在独立的地方的一个工具,它给这些工程创建虚拟的 Python环境。它解决了“项目X依赖于版本1.x,而项目Y需要项目4.x”的两难问题,而且使你的全局 site-packages 目录保持干净和可管理。软件工程师全都是有洁癖的,这就出现下面的神器 virtualenv。

安装 virtualenv

首先,我们用 pip 安装 virtualenv:

$ pip install virtualenv

测试你的安装

$ virtualenv --version

进入我们的项目目录,创建一个独立的 Python 运行环境,命名为 venv。virtualenv venv 将会在当前的目录中创建一个文件夹包含了 Python 可执行文件, 以及 pip 库的一份拷贝,这样就能安装其他包了。虚拟环境的名字(此例中是 venv ) 可以是任意的;若省略名字将会把文件均放在当前目录。

$ cd /data/myproject
$ virtualenv --no-site-packages venv
Using base prefix '/usr/local/.../Python.framework/Versions/3.4'
New python executable in venv/bin/python3.4
Also creating executable in venv/bin/python
Installing setuptools, pip, wheel...done.

你可以选择使用一个Python解释器(比如python2.7):

$ virtualenv -p /usr/bin/python2.7 venv

命令 virtualenv 就可以创建一个独立的 Python 运行环境,我们还加上了参数 –no-site-packages,这样,已经安装到系统 Python 环境中的所有第三方包都不会复制过来,这样,我们就得到了一个不带任何第三方包的“干净”的 Python 运行环境。

进入独立环境

新建的 Python 环境被放到当前目录下的 venv 目录。有了 venv 这个 Python 环境,可以用 source 进入该环境:

$ source venv/bin/activate
(venv) $

以下命令会有相同效果:

$ . ./venv/bin/activate
(venv) $

注意到命令提示符变了,有个 (venv) 前缀,表示当前环境是一个名为 venv 的 Python 环境。

下面就可以正常安装各种第三方扩展包,并运行 python 命令:

(venv) $ pip3 install flask

在 venv 环境下,用 pip 安装的包都被安装到 venv 这个环境下,系统 Python 环境不受任何影响。也就是说,venv 环境是专门针对 myproject 这个应用创建的。

退出独立环境

退出当前的 venv 环境,使用 deactivate 命令:

(venv) $ deactivate
$

此时就回到了正常的环境,现在 pip 或 python 均是在系统 Python 环境下执行。

完全可以针对每个应用创建独立的 Python 运行环境,这样就可以对每个应用的 Python 环境进行隔离。

virtualenv 是如何创建“独立”的 Python 运行环境的呢?原理很简单,就是把系统 Python 复制一份到 virtualenv 的环境,用命令 source venv/bin/activate 进入一个 virtualenv 环境时,virtualenv 会修改相关环境变量,让命令 python 和 pip 均指向当前的 virtualenv 环境。

自动激活环境工具 autoenv

当你 cd 进入一个包含 .env 的目录中,就会 autoenv 自动激活那个环境。

使用 brew 在 Mac OS X上安装它:

$ brew install autoenv

在 Linux 上:

$ git clone git://github.com/kennethreitz/autoenv.git ~/.autoenv
$ echo 'source ~/.autoenv/activate.sh' >> ~/.bashrc

项目地址:https://github.com/kennethreitz/autoenv/

其它注意

运行带 –no-site-packages 选项的 virtualenv 将不会包括全局安装的包。 这可用于保持包列表干净,以防以后需要访问它。(这在 virtualenv 1.7及之后是默认行为)

python sqlalchemy执行原始sql语句示例

参考文档

官方文档首页: http://docs.sqlalchemy.org/en/latest/

连接数据库

这边使用sqlite的内存数据库,方便测试

import sqlalchemy

db_engine = sqlalchemy.create_engine('sqlite:///:memory:', echo=True)
db_conn = db_engine.connect()

使用create_engine可以建立一个连接池,调用connect可以从连接池中获取一个连接,调用db_conn.close()会把连接释放给连接池,并做相应的清理工作。

官方说明:http://docs.sqlalchemy.org/en/latest/core/connections.html#basic-usage

The connection is an instance of Connection, which is a proxy object for an actual DBAPI connection. The DBAPI connection is retrieved from the connection pool at the point at which Connection is created.
The returned result is an instance of ResultProxy, which references a DBAPI cursor and provides a largely compatible interface with that of the DBAPI cursor. The DBAPI cursor will be closed by the ResultProxy when all of its result rows (if any) are exhausted. A ResultProxy that returns no rows, such as that of an UPDATE statement (without any returned rows), releases cursor resources immediately upon construction.
When the close() method is called, the referenced DBAPI connection is released to the connection pool. From the perspective of the database itself, nothing is actually “closed”, assuming pooling is in use. The pooling mechanism issues a rollback() call on the DBAPI connection so that any transactional state or locks are removed, and the connection is ready for its next usage.

使用原始SQL

先进行建表操作

db_conn.execute(r'''
CREATE TABLE IF NOT EXISTS stocks (
        date text, 
        trans text, 
        symbol text, 
        qty real, 
        pricereal
    )
''')

插入单条数据

直接拼装一个完整的sql字符串,或使用下面防注入的参数绑定方式, 注意:会自动commit

db_conn.execute(r'''
INSERT INTO stocks VALUES (?, ?, ?, ?, ?)
''', ('2001-01-11', 'BUY', 'RHAT', 100, 35.14) )
db_conn.execute(r'''
INSERT INTO stocks VALUES (:1, :2, :3, :4, :5)
''', ('2001-01-11', 'BUY', 'RHAT', 100, 35.14) )

操作日志显示会自动commit

2017-08-27 15:05:26,834 INFO sqlalchemy.engine.base.Engine ()
2017-08-27 15:05:26,834 INFO sqlalchemy.engine.base.Engine COMMIT
2017-08-27 15:05:26,834 INFO sqlalchemy.engine.base.Engine 
INSERT INTO stocks VALUES (:1, :2, :3, :4, :5)

2017-08-27 15:05:26,834 INFO sqlalchemy.engine.base.Engine ('2001-01-11', 'BUY', 'RHAT', 100, 35.14)
2017-08-27 15:05:26,834 INFO sqlalchemy.engine.base.Engine COMMIT
2017-08-27 15:05:26,835 INFO sqlalchemy.engine.base.Engine 
INSERT INTO stocks VALUES (?, ?, ?, ?, ?)

2017-08-27 15:05:26,835 INFO sqlalchemy.engine.base.Engine ('2001-01-11', 'BUY', 'RHAT', 100, 35.14)
2017-08-27 15:05:26,835 INFO sqlalchemy.engine.base.Engine COMMIT
2017-08-27 15:05:26,835 INFO sqlalchemy.engine.base.Engine 
INSERT INTO stocks VALUES (:1, :2, :3, :4, :5)

插入多条数据

与插入单条数据类似, 注意:会自动commit

purchases = [('2006-03-28', 'BUY', 'IBM', 1000, 45.00),
             ('2006-04-05', 'BUY', 'MSFT', 1000, 72.00),
             ('2006-04-06', 'SELL', 'IBM', 500, 53.00),
            ]
db_conn.execute(r'''
INSERT INTO stocks VALUES (:1, :2, :3, :4, :5)
''', purchases)
db_conn.execute(r'''
INSERT INTO stocks VALUES (?, ?, ?, ?, ?)
''', purchases)

操作日志显示会自动commit

2017-08-27 15:05:26,835 INFO sqlalchemy.engine.base.Engine 
INSERT INTO stocks VALUES (:1, :2, :3, :4, :5)

2017-08-27 15:05:26,835 INFO sqlalchemy.engine.base.Engine [('2006-03-28', 'BUY', 'IBM', 1000, 45.0), ('2006-04-05', 'BUY', 'MSFT', 1000, 72.0), ('2006-04-06', 'SELL', 'IBM', 500, 53.0)]
2017-08-27 15:05:26,835 INFO sqlalchemy.engine.base.Engine COMMIT
2017-08-27 15:05:26,835 INFO sqlalchemy.engine.base.Engine 
INSERT INTO stocks VALUES (?, ?, ?, ?, ?)

2017-08-27 15:05:26,835 INFO sqlalchemy.engine.base.Engine [('2006-03-28', 'BUY', 'IBM', 1000, 45.0), ('2006-04-05', 'BUY', 'MSFT', 1000, 72.0), ('2006-04-06', 'SELL', 'IBM', 500, 53.0)]
2017-08-27 15:05:26,835 INFO sqlalchemy.engine.base.Engine COMMIT

使用事务处理

参考文档: http://docs.sqlalchemy.org/en/latest/core/connections.html#using-transactions

由于上面的插入操作会自动进行commit,sqlalchemy提供了Transactions来管理commit和rollback。

完整的测试代码如下

import sqlalchemy

db_engine = sqlalchemy.create_engine('sqlite:///:memory:', echo=True, pool_size=2)
db_conn = db_engine.connect()

db_conn.execute(r'''
CREATE TABLE IF NOT EXISTS stocks (date text, trans text, symbol text, qty real, price real)
''')

with db_conn.begin() as db_trans:
    #
    db_conn.execute(r'''
    INSERT INTO stocks VALUES (:1, :2, :3, :4, :5)
    ''', ('aa', 'BUY', 'RHAT', 100, 35.14) )

    db_conn.execute(r'''
    INSERT INTO stocks VALUES (?, ?, ?, ?, ?)
    ''', ('bb', 'BUY', 'RHAT', 100, 35.14) )

    #提前进行commit
    db_trans.commit()

    purchases = [('cc', 'BUY', 'IBM', 1000, 45.00),
                 ('dd', 'BUY', 'MSFT', 1000, 72.00),
                 ('ee', 'SELL', 'IBM', 500, 53.00),
                ]
    db_conn.execute(r'''
    INSERT INTO stocks VALUES (?,?,?,?,?)
    ''', purchases)


query_result = db_conn.execute(r'''
SELECT * FROM  stocks
''').fetchall()
print('query_result:', query_result)

完整的日志如下

2017-08-27 15:18:22,057 INFO sqlalchemy.engine.base.Engine SELECT CAST('test plain returns' AS VARCHAR(60)) AS anon_1
2017-08-27 15:18:22,057 INFO sqlalchemy.engine.base.Engine ()
2017-08-27 15:18:22,057 INFO sqlalchemy.engine.base.Engine SELECT CAST('test unicode returns' AS VARCHAR(60)) AS anon_1
2017-08-27 15:18:22,057 INFO sqlalchemy.engine.base.Engine ()
2017-08-27 15:18:22,058 INFO sqlalchemy.engine.base.Engine 
CREATE TABLE IF NOT EXISTS stocks (date text, trans text, symbol text, qty real, price real)

2017-08-27 15:18:22,058 INFO sqlalchemy.engine.base.Engine ()
2017-08-27 15:18:22,058 INFO sqlalchemy.engine.base.Engine COMMIT
2017-08-27 15:18:22,058 INFO sqlalchemy.engine.base.Engine BEGIN (implicit)
2017-08-27 15:18:22,058 INFO sqlalchemy.engine.base.Engine 
    INSERT INTO stocks VALUES (:1, :2, :3, :4, :5)

2017-08-27 15:18:22,058 INFO sqlalchemy.engine.base.Engine ('aa', 'BUY', 'RHAT', 100, 35.14)
2017-08-27 15:18:22,058 INFO sqlalchemy.engine.base.Engine 
    INSERT INTO stocks VALUES (?, ?, ?, ?, ?)

2017-08-27 15:18:22,059 INFO sqlalchemy.engine.base.Engine ('bb', 'BUY', 'RHAT', 100, 35.14)
2017-08-27 15:18:22,059 INFO sqlalchemy.engine.base.Engine COMMIT
2017-08-27 15:18:22,059 INFO sqlalchemy.engine.base.Engine 
    INSERT INTO stocks VALUES (?,?,?,?,?)

2017-08-27 15:18:22,059 INFO sqlalchemy.engine.base.Engine [('cc', 'BUY', 'IBM', 1000, 45.0), ('dd', 'BUY', 'MSFT', 1000, 72.0), ('ee', 'SELL', 'IBM', 500, 53.0)]
2017-08-27 15:18:22,059 INFO sqlalchemy.engine.base.Engine COMMIT
2017-08-27 15:18:22,059 INFO sqlalchemy.engine.base.Engine 
SELECT * FROM  stocks

2017-08-27 15:18:22,059 INFO sqlalchemy.engine.base.Engine ()
query_result: [('aa', 'BUY', 'RHAT', 100.0, 35.14), ('bb', 'BUY', 'RHAT', 100.0, 35.14), ('cc', 'BUY', 'IBM', 1000.0, 45.0), ('dd', 'BUY', 'MSFT', 1000.0, 72.0), ('ee', 'SELL', 'IBM', 500.0, 53.0)]

可以看到有三次commit操作,有三次commit操作, 一次是建表, 两次是插入,其中第二次是显示调用commit,第三次是with结束后自动调用。因此如果想避免频繁的commit,可以使用with来进行上下文管理。

测试异常

import sqlalchemy

db_engine = sqlalchemy.create_engine('sqlite:///:memory:', echo=True, pool_size=2)
db_conn = db_engine.connect()

try:
    with db_conn.begin() as db_trans:
        db_conn.execute(r'''
        CREATE TABLE IF NOT EXISTS stocks (date text, trans text, symbol text, qty real, price real)
        ''')

        db_conn.execute(r'''
        INSERT INTO stocks VALUES (:1, :2, :3, :4, :5)
        ''', ('aa', 'BUY', 'RHAT', 100, 35.14) )
        db_trans.commit()

        db_conn.execute(r'''
        INSERT INTO stocks VALUES (:1, :2, :3, :4, :5)
        ''', ('bb', 'BUY', 'RHAT', 100, 35.14) )

        db_conn.execute(r'''
        INSERT INTO stocks VALUES (:1, :2, :3, :4, :5)
        ''', ('cc', 'BUY', 'RHAT', 100) ) #这里故意少传一个参数来制造异常
except:
    print('exception!!!')


query_result = db_conn.execute(r'''
SELECT * FROM  stocks
''').fetchall()
print('query_result:', query_result)

日志

2017-08-27 15:40:53,151 INFO sqlalchemy.engine.base.Engine SELECT CAST('test plain returns' AS VARCHAR(60)) AS anon_1
2017-08-27 15:40:53,151 INFO sqlalchemy.engine.base.Engine ()
2017-08-27 15:40:53,152 INFO sqlalchemy.engine.base.Engine SELECT CAST('test unicode returns' AS VARCHAR(60)) AS anon_1
2017-08-27 15:40:53,152 INFO sqlalchemy.engine.base.Engine ()
2017-08-27 15:40:53,152 INFO sqlalchemy.engine.base.Engine BEGIN (implicit)
2017-08-27 15:40:53,152 INFO sqlalchemy.engine.base.Engine 
        CREATE TABLE IF NOT EXISTS stocks (date text, trans text, symbol text, qty real, price real)

2017-08-27 15:40:53,153 INFO sqlalchemy.engine.base.Engine ()
2017-08-27 15:40:53,153 INFO sqlalchemy.engine.base.Engine 
        INSERT INTO stocks VALUES (:1, :2, :3, :4, :5)

2017-08-27 15:40:53,153 INFO sqlalchemy.engine.base.Engine ('aa', 'BUY', 'RHAT', 100, 35.14)
2017-08-27 15:40:53,153 INFO sqlalchemy.engine.base.Engine COMMIT
2017-08-27 15:40:53,153 INFO sqlalchemy.engine.base.Engine 
        INSERT INTO stocks VALUES (:1, :2, :3, :4, :5)

2017-08-27 15:40:53,153 INFO sqlalchemy.engine.base.Engine ('bb', 'BUY', 'RHAT', 100, 35.14)
2017-08-27 15:40:53,153 INFO sqlalchemy.engine.base.Engine COMMIT
2017-08-27 15:40:53,153 INFO sqlalchemy.engine.base.Engine 
        INSERT INTO stocks VALUES (:1, :2, :3, :4, :5)

2017-08-27 15:40:53,153 INFO sqlalchemy.engine.base.Engine ('cc', 'BUY', 'RHAT', 100)
2017-08-27 15:40:53,154 INFO sqlalchemy.engine.base.Engine ROLLBACK
exception!!!
2017-08-27 15:40:53,154 INFO sqlalchemy.engine.base.Engine 
SELECT * FROM  stocks

2017-08-27 15:40:53,154 INFO sqlalchemy.engine.base.Engine ()
query_result: [('aa', 'BUY', 'RHAT', 100.0, 35.14), ('bb', 'BUY', 'RHAT', 100.0, 35.14)]
  • 出现异常时会自动rollback
  • 如果在with中提前调用commit,会导致以后的每个改动操作都会自动commit

tornado分页实现-从本质到完全实现

初始化tornado目录

未分类

构建项目–实现post提交,get展示数据

实现类似

未分类

代码逻辑

未分类

完整代码

start.py

#!/usr/bin/env python
# coding=utf-8
import time
import tornado.ioloop
import tornado.web


# 业务逻辑处理模块


# 配置选项模块
from controllers import home


# 静态文件和模板文件的配置
settings = {
    'template_path': 'templates',
    'static_path': 'statics',
}

# 路由模块
## 动态路由系统
application = tornado.web.Application([
    (r"/index/(?P<page>d+)/(?P<nid>d+)", home.IndexHandler),
],
    **settings
)

## wsgi模块
if __name__ == "__main__":
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()
controllers/home.py

#!/usr/bin/env python
# coding=utf-8
import tornado.web

LIST_INFO = [
    {"username": "maotai", "email": "123456"},
]


class IndexHandler(tornado.web.RequestHandler):
    # def get(self, *args, **kwargs):
    def get(self,page,nid):
        # print(page,nid)
        self.render("home/index.html", list_info=LIST_INFO)

    def post(self, *args, **kwargs):
        username = self.get_argument("username", "")
        email = self.get_argument("email", "")

        tmp = {"username": username, "email": email}
        LIST_INFO.append(tmp)
        self.redirect("/index/1/1")
home/index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>index</title>
</head>
<body>
<h1>添加数据</h1>
<form action="/index/1/1" method="post">
    <input type="text" name="username">
    <input type="email" name="email">
    <input type="submit" typeof="提交">
</form>
<h1>显示数据</h1>
<table border="1">
    <thead>
        <tr>
            <th>用户名</th>
            <th>邮箱</th>
        </tr>
    </thead>
    <tbody>
        {% for line in list_info %}
            <tr>
                <td>{{ line['username'] }}</td>
                <td>{{ line['email'] }}</td>
            </tr>
        {% end %}
    </tbody>
</table>
</body>
</html>

实现url //

未分类

实现分页-每页显示5条数据

效果:

未分类

代码逻辑:

未分类

完整代码:

#!/usr/bin/env python
# coding=utf-8
import time
import tornado.ioloop
import tornado.web


# 业务逻辑处理模块


# 配置选项模块
from controllers import home


# 静态文件和模板文件的配置
settings = {
    'template_path': 'templates',
    'static_path': 'statics',
}

# 路由模块
## 动态路由系统
application = tornado.web.Application([
    (r"/index/(?P<page>d*)", home.IndexHandler),
],
    **settings
)

## wsgi模块
if __name__ == "__main__":
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()
#!/usr/bin/env python
# coding=utf-8
import tornado.web

LIST_INFO = [
    {"username": "maotai", "email": "123456"},
]


class IndexHandler(tornado.web.RequestHandler):
    # def get(self, *args, **kwargs):
    def get(self, page):
        # 每页显示5条数据
        # LIST_INFO[0:5]
        # LIST_INFO[5:10]
        page = int(page)
        print(page)
        start = (page - 1) * 5
        end = page * 5
        current_list = LIST_INFO[start:end]
        self.render("home/index.html", list_info=current_list)

    def post(self, *args, **kwargs):
        username = self.get_argument("username", "")
        email = self.get_argument("email", "")

        tmp = {"username": username, "email": email}
        LIST_INFO.append(tmp)
        self.redirect("/index/1")
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>index</title>
</head>
<body>
<h1>添加数据</h1>
<form action="/index/1" method="post">
    <input type="text" name="username">
    <input type="email" name="email">
    <input type="submit" typeof="提交">
</form>
<h1>显示数据</h1>
<table border="1">
    <thead>
        <tr>
            <th>用户名</th>
            <th>邮箱</th>
        </tr>
    </thead>
    <tbody>
        {% for line in list_info %}
            <tr>
                <td>{{ line['username'] }}</td>
                <td>{{ line['email'] }}</td>
            </tr>
        {% end %}
    </tbody>
</table>
</body>
</html>

处理page异常

  • page为空 如page=
  • page转换int不成功, 如page=xfdsfd213
  • page<=0情况

思路:

使用try except来操作

class IndexHandler(tornado.web.RequestHandler):
    # def get(self, *args, **kwargs):
    def get(self, page):
        # 每页显示5条数据
        # LIST_INFO[0:5]
        # LIST_INFO[5:10]

        # c        
        # if not page:
        #     page = 1
        try:
            page = int(page)
        except:
            page = 1
        if page <= 0:
            page = 1

        print(page)
        start = (page - 1) * 5
        end = page * 5
        current_list = LIST_INFO[start:end]
        self.render("home/index.html", list_info=current_list)

    def post(self, *args, **kwargs):
        username = self.get_argument("username", "")
        email = self.get_argument("email", "")

        tmp = {"username": username, "email": email}
        LIST_INFO.append(tmp)
        self.redirect("/index/1")

实现提交数据后,不总是跳转到index/1,而是和get时候index/x一样

实现效果

未分类

代码逻辑

未分类

完整代码:

#!/usr/bin/env python
# coding=utf-8
import tornado.web

LIST_INFO = [
    {"username": "maotai", "email": "123456"},
]


class IndexHandler(tornado.web.RequestHandler):
    # def get(self, *args, **kwargs):
    def get(self, page):
        try:
            page = int(page)
        except:
            page = 1
        if page <= 0:
            page = 1

        print(page)
        start = (page - 1) * 5
        end = page * 5
        current_list = LIST_INFO[start:end]
        self.render("home/index.html", list_info=current_list,current_page=page)

    def post(self,page):
        username = self.get_argument("username", "")
        email = self.get_argument("email", "")

        tmp = {"username": username, "email": email}
        LIST_INFO.append(tmp)
        self.redirect("/index/"+page)
#!/usr/bin/env python
# coding=utf-8
import time
import tornado.ioloop
import tornado.web


# 业务逻辑处理模块


# 配置选项模块
from controllers import home


# 静态文件和模板文件的配置
settings = {
    'template_path': 'templates',
    'static_path': 'statics',
}

# 路由模块
## 动态路由系统
application = tornado.web.Application([
    (r"/index/(?P<page>d*)", home.IndexHandler),
],
    **settings
)

## wsgi模块
if __name__ == "__main__":
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>index</title>
</head>
<body>
<h1>添加数据</h1>
<form action="/index/{{ current_page }}" method="post">
    <input type="text" name="username">
    <input type="email" name="email">
    <input type="submit" typeof="提交">
</form>
<h1>显示数据</h1>
<table border="1">
    <thead>
        <tr>
            <th>用户名</th>
            <th>邮箱</th>
        </tr>
    </thead>
    <tbody>
        {% for line in list_info %}
            <tr>
                <td>{{ line['username'] }}</td>
                <td>{{ line['email'] }}</td>
            </tr>
        {% end %}
    </tbody>
</table>
</body>
</html>

xss跨站

效果: 提交js代码.每刷新一次都会执行一次.

未分类

实现:让代码按照原格式执行,前面加raw

    <tbody>
        {% for line in list_info %}
            <tr>
                <!--<td>{{ line['username'] }}</td>-->
                <td>{% raw line['username'] %}</td>
                <td>{{ line['email'] }}</td>
            </tr>
        {% end %}
    </tbody>

初步实现点分页链接

效果:

未分类

核心代码

<table border="1">
    <thead>
        <tr>
            <th>用户名</th>
            <th>邮箱</th>
        </tr>
    </thead>
    <tbody>
        {% for line in list_info %}
            <tr>
                <!--<td>{{ line['username'] }}</td>-->
                <td>{% raw line['username'] %}</td>
                <td>{{ line['email'] }}</td>
            </tr>
        {% end %}
    </tbody>
</table>
<div class = "pager">
    <a href="/index/1">1</a>
    <a href="/index/2">2</a>
    <a href="/index/3">3</a>
</div>

从后端传值过来

实现效果

未分类

    def get(self, page):
        try:
            page = int(page)
        except:
            page = 1
        if page <= 0:
            page = 1


        start = (page - 1) * 5
        end = page * 5
        current_list = LIST_INFO[start:end]

        all_pager,c = divmod(len(LIST_INFO),5)
        if c>0:
            all_pager+=1

        str_page = """
        <a href="/index/1">1</a>
        <a href="/index/2">2</a>
        <a href="/index/3">3</a>
        <a href="/index/4">4</a>
        """
        self.render("home/index.html", list_info=current_list,current_page=page,str_page=str_page)

实现分页链接根据后端数据长度自动生成

效果图:

未分类

核心代码:

LIST_INFO = [
    {"username": "maotai", "email": "123456"},
]
for line in range(300):
    tmp={"username": "maotai", "email": "123456"}
    LIST_INFO.append(tmp)
    def get(self, page):
        try:
            page = int(page)
        except:
            page = 1
        if page <= 0:
            page = 1


        start = (page - 1) * 5
        end = page * 5
        current_list = LIST_INFO[start:end]

        all_pager,c = divmod(len(LIST_INFO),5)
        if c>0:
            all_pager+=1

        # str_page = """
        # <a href="/index/1">1</a>
        # <a href="/index/2">2</a>
        # <a href="/index/3">3</a>
        # <a href="/index/4">4</a>
        # """
        list_page=[]
        for p in range(all_pager):
            tmp = '<a href="/index/%s">%s</a>'%(p+1,p+1)
            list_page.append(tmp)
        str_page="".join(list_page)
        self.render("home/index.html", list_info=current_list,current_page=page,str_page=str_page)
<div class = "pager">
    <!--{{str_page}}-->
    {% raw str_page %}
</div>

实现当前页分页链接高亮

未分类

    def get(self, page):
        try:
            page = int(page)
        except:
            page = 1
        if page <= 0:
            page = 1


        start = (page - 1) * 5
        end = page * 5
        current_list = LIST_INFO[start:end]

        all_pager,c = divmod(len(LIST_INFO),5)
        if c>0:
            all_pager+=1

        # str_page = """
        # <a href="/index/1">1</a>
        # <a href="/index/2">2</a>
        # <a href="/index/3">3</a>
        # <a href="/index/4">4</a>
        # """
        list_page=[]
        for p in range(all_pager):
            if p+1 == page:
                tmp = '<a class="active" href="/index/%s">%s</a>' % (p + 1, p + 1)
            else:
                tmp = '<a href="/index/%s">%s</a>'%(p+1,p+1)
            list_page.append(tmp)
        str_page="".join(list_page)
        self.render("home/index.html", list_info=current_list,current_page=page,str_page=str_page)

前端:

        .pager a.active{
            background-color: brown;
            color: white;
        }

完整的code:

未分类

#!/usr/bin/env python
# coding=utf-8
import tornado.web

LIST_INFO = [
    {"username": "maotai", "email": "123456"},
]
for line in range(300):
    tmp={"username": "maotai", "email": "123456"}
    LIST_INFO.append(tmp)

class IndexHandler(tornado.web.RequestHandler):
    # def get(self, *args, **kwargs):
    def get(self, page):
        try:
            page = int(page)
        except:
            page = 1
        if page <= 0:
            page = 1


        start = (page - 1) * 5
        end = page * 5
        current_list = LIST_INFO[start:end]

        all_pager,c = divmod(len(LIST_INFO),5)
        if c>0:
            all_pager+=1

        # str_page = """
        # <a href="/index/1">1</a>
        # <a href="/index/2">2</a>
        # <a href="/index/3">3</a>
        # <a href="/index/4">4</a>
        # """
        list_page=[]
        for p in range(all_pager):
            if p+1 == page: #这里为什么p+1,因为遍历的是从0到all_pager
                tmp = '<a class="active" href="/index/%s">%s</a>' % (p + 1, p + 1)
            else:
                tmp = '<a href="/index/%s">%s</a>'%(p+1,p+1)
            list_page.append(tmp)
        str_page="".join(list_page)
        self.render("home/index.html", list_info=current_list,current_page=page,str_page=str_page)

    def post(self,page):
        username = self.get_argument("username", "")
        email = self.get_argument("email", "")

        tmp = {"username": username, "email": email}
        LIST_INFO.append(tmp)
        self.redirect("/index/"+page)
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>index</title>
    <style>
        .pager a{
            display: inline-block;
            padding:5px;
            margin: 3px;
            background-color: cadetblue;
        }
        .pager a.active{
            background-color: brown;
            color: white;
        }
    </style>
</head>
<body>
<h1>添加数据</h1>
<form action="/index/{{ current_page }}" method="post">
    <input type="text" name="username">
    <input type="email" name="email">
    <input type="submit" typeof="提交">
</form>
<h1>显示数据</h1>
<table border="1">
    <thead>
        <tr>
            <th>用户名</th>
            <th>邮箱</th>
        </tr>
    </thead>
    <tbody>
        {% for line in list_info %}
            <tr>
                <td>{{ line['username'] }}</td>
                <!--<td>{% raw line['username'] %}</td>-->
                <td>{{ line['email'] }}</td>
            </tr>
        {% end %}
    </tbody>
</table>
<div class = "pager">
    <!--{{str_page}}-->
    {% raw str_page %}
</div>
</body>
</html>

实现显示11项,当前页+-5

未分类

分析:

当总页数<11:
    1 11
当总页数>11:
    当前页<6:
        1 11
    当前页>6:
        当前页+5>总页数
            总页数-11 总页数
        当前页+5<总页数:
            s=page-5
            t=page+5

核心实现代码:

class IndexHandler(tornado.web.RequestHandler):
    # def get(self, *args, **kwargs):
    def get(self, page):
        try:
            page = int(page)
        except:
            page = 1
        if page <= 0:
            page = 1

        start = (page - 1) * 5
        end = page * 5
        current_list = LIST_INFO[start:end]

        all_pager, c = divmod(len(LIST_INFO), 5)
        if c > 0:
            all_pager += 1

        # str_page = """
        # <a href="/index/1">1</a>
        # <a href="/index/2">2</a>
        # <a href="/index/3">3</a>
        # <a href="/index/4">4</a>
        # """
        list_page = []
        if all_pager < 11:
            s = 1
            t = 11
        else:
            if page <= 6:
                s = 1
                t = 11
            elif page > 6:
                if page + 5 > all_pager:
                    s = all_pager - 11
                    t = all_pager
                else:
                    s = page - 5
                    t = page + 5

        for p in range(s, t + 1):
            if p == page:  # 这里为什么p,而不是p+1? 因为range(s, t + 1),不是从0开始的.
                tmp = '<a class="active" href="/index/%s">%s</a>' % (p, p)
            else:
                tmp = '<a href="/index/%s">%s</a>' % (p, p)
            list_page.append(tmp)
        str_page = "".join(list_page)
        self.render("home/index.html", list_info=current_list, current_page=page, str_page=str_page)

封装分页class

思路

未分类

代码:

#!/usr/bin/env python
# coding=utf-8
import tornado.web

LIST_INFO = [
    {"username": "maotai", "email": "123456"},
]
for line in range(300):
    tmp = {"username": "maotai", "email": "123456"}
    LIST_INFO.append(tmp)


class Pagination:
    def __init__(self, current_page, all_item):
        all_pager, c = divmod(len(all_item), 5)
        if c > 0:
            all_pager += 1
        try:
            current_page = int(current_page)
        except:
            current_page = 1
        if current_page <= 0:
            current_page = 1

        self.current_page = current_page
        self.all_pager = all_pager

    @property
    def start(self):
        return (self.current_page - 1) * 5

    @property
    def end(self):
        return self.current_page * 5

    def page_str(self, baseurl):
        list_page = []
        if self.all_pager < 11:
            s = 1
            t = 11
        else:
            if self.current_page <= 6:
                s = 1
                t = 11
            elif self.current_page > 6:
                if self.current_page + 5 > self.all_pager:
                    s = self.all_pager - 11
                    t = self.all_pager
                else:
                    s = self.current_page - 5
                    t = self.current_page + 5

        for p in range(s, t + 1):
            if p == self.current_page:
                tmp = '<a class="active" href="%s%s">%s</a>' % (baseurl, p, p)
            else:
                tmp = '<a href="%s%s">%s</a>' % (baseurl, p, p)
            list_page.append(tmp)
        return "".join(list_page)

    def current_list_info(self):
        current_list = LIST_INFO[self.start:self.end]
        return current_list


class IndexHandler(tornado.web.RequestHandler):
    # def get(self, *args, **kwargs):
    def get(self, page):
        ## 实例化分页类
        # 传入当前页 和 所有数据
        # 获得current_list 即每页5条数据列表
        page_obj = Pagination(page, LIST_INFO)
        current_list = page_obj.current_list_info()
        str_page = page_obj.page_str("/index/")
        self.render("home/index.html", list_info=current_list, current_page=page, str_page=str_page)

    def post(self, page):
        username = self.get_argument("username", "")
        email = self.get_argument("email", "")

        tmp = {"username": username, "email": email}
        LIST_INFO.append(tmp)
        self.redirect("/index/" + page)

实现上一页 下一页 跳转到

效果:

未分类

原理:

未分类

利用js的方法href实现跳转.

完整的code:

#!/usr/bin/env python
# coding=utf-8
import tornado.web

LIST_INFO = [
    {"username": "maotai", "email": "123456"},
]
for line in range(300):
    tmp = {"username": "maotai", "email": "123456"}
    LIST_INFO.append(tmp)


class Pagination:
    def __init__(self, current_page, all_item):
        all_pager, c = divmod(len(all_item), 5)
        if c > 0:
            all_pager += 1
        try:
            current_page = int(current_page)
        except:
            current_page = 1
        if current_page <= 0:
            current_page = 1

        self.current_page = current_page
        self.all_pager = all_pager
        self.all_item=all_item

    @property
    def start(self):
        return (self.current_page - 1) * 5

    @property
    def end(self):
        return self.current_page * 5

    def current_list_info(self):
        current_list = LIST_INFO[self.start:self.end]
        return current_list

    def page_str(self, baseurl):
        list_page = []

        # 上一页  --上一页 页码 下一页 必须按照顺序添加到list
        if self.current_page > 1:
            last_page = '<a href="%s%s">上一页</a>' % (baseurl, self.current_page - 1)
        else:
            last_page = '<a href="%s%s">上一页</a>' % (baseurl, self.current_page)
        list_page.append(last_page)

        # 页码页
        if self.all_pager < 11:
            s = 1
            t = 11
        else:
            if self.current_page <= 6:
                s = 1
                t = 11
            elif self.current_page > 6:
                if self.current_page + 5 > self.all_pager:
                    s = self.all_pager - 11
                    t = self.all_pager
                else:
                    s = self.current_page - 5
                    t = self.current_page + 5

        for p in range(s, t + 1):

            if p == self.current_page:
                tmp = '<a class="active" href="%s%s">%s</a>' % (baseurl, p, p)
            else:
                tmp = '<a href="%s%s">%s</a>' % (baseurl, p, p)
            list_page.append(tmp)

        # if self.current_page>1:
        #     syyp = self.current_page - 1
        # else:
        #     syyp = self.current_page
        #
        # if self.current_page > len(self.all_item):
        #     xyyp = len(self.all_item)
        # else:
        #     xyyp = self.current_page
        #
        # syy = '<a href="%s%s">上一页</a>' % (baseurl, syyp)
        # xyy = '<a href="%s%s">下一页</a>' % (baseurl, xyyp)
        # list_page.insert(0,syy)
        # list_page.append(xyy)



        # 下一页
        if self.current_page< self.all_pager:
            next_page = '<a href="%s%s">下一页</a>' % (baseurl, self.current_page+1)
        else:
            next_page = '<a href="%s%s">下一页</a>' % (baseurl, self.current_page)
        list_page.append(next_page)

        # 跳转到
        ## 获取本次点击前框里输入的数据, 拼接 baseurl + 数字 就可以实现跳转
        jump = """<input type="text" /><a onclick="Jump('%s',this);">GO</a>"""%(baseurl,)
        script="""<script>
            function Jump(baseUrl,ths){
                var val = ths.previousElementSibling.value;
                if(val.trim().length>0){
                    location.href = baseUrl+val;
                }
            }
            </script>
        """
        list_page.append(jump)
        list_page.append(script)
        return "".join(list_page)


class IndexHandler(tornado.web.RequestHandler):
    # def get(self, *args, **kwargs):
    def get(self, page):
        page_obj = Pagination(page, LIST_INFO)
        current_list = page_obj.current_list_info()
        str_page = page_obj.page_str("/index/")
        self.render("home/index.html", list_info=current_list, current_page=page, str_page=str_page)

    def post(self, page):
        username = self.get_argument("username", "")
        email = self.get_argument("email", "")

        tmp = {"username": username, "email": email}
        LIST_INFO.append(tmp)
        self.redirect("/index/" + page)