apache默认对进行了编码的url 返回 404

我们通常使用 urlencode()之类的函数将斜线编码成%开头的字符串

但是默认情况下

apache发现请求的URL中有对斜线的编码后的字符,是会返回404页面的

此时,就用到了 AllowEncodedSlashes on 指令允许请求继续被处理

如果apache配置的https,那么http和https需要分别配置。

我用的apache是 Apache/2.4.6 (CentOS)

http的配置文件和ssl配置文件是分开的。

在httpd.conf 中直接在文件中增加 AllowEncodedSlashes On

重启就生效了,而https 直接放到配置ssl.conf中是没有生效。后来查到是放到 这个标签里边。

重启后终于生效。

未分类

Apache防盗链和隐藏版本信息

实验要求:

三台虚拟机分别是:linux和两台windows虚拟机,linux虚拟机为服务器,Windows7-1为客户端,Windows7-2为盗链端。

实验步骤:

一、防盗链

1.把httpd、apr、apr-util安装包解压缩到/opt目录中

tar zxvf apr-1.4.6.tar.gz -C /opt/
tar zxvf apr-util-1.4.1.tar.gz -C /opt/
tar zxvf httpd-2.4.2.tar.gz -C /opt/

2.切换到opt目录中,把解压的文件夹递归复制到apr文件夹中

未分类

3.用yum仓库安装gcc 、gcc-c++ 、pcre 、pcre-devel、zlib-devel工具包

未分类

4.进行配置文件的配置

未分类

5.转换为二进制文件且进行安装

未分类

6.开启httpd功能且建立软连

未分类

未分类

7.在httpd的主配置文件/etc/httpd.conf 下修改监听本地主机的IP和主机名域名

未分类

未分类

8.开启httpd服务关闭防火墙和增强性安全功能

service httpd start 
service iptables stop
Setenforce 0

9.切换到/usr/local/httpd/htdocs/目录下编辑首页 文件

未分类

未分类

10.把/opt目录下的图片(game.jpg、error.png)复制到当前目录下

未分类

未分类

11.打开windows7-2盗链客户端,创建文件添加首页文件和图片且更改为扩展文件,放置于C盘inetpub wwwroot目录中。

未分类

未分类

12.构建DNS解析服务,安装bind包,编辑主配置文件/etc/named.conf修改监听地址。

未分类

未分类

13.编辑区域配置文件/etc/named.rfc1912.zones 添加域名和区域数据模块

未分类

14.复制区域数据模板到benet.com.zone进行编辑

未分类

未分类

15.启动named服务

未分类

16.在站点目录/usr/local/httpd/conf/编辑vim httpd.conf文件中开启重写模块且引用防盗链规则

未分类

未分类

17.重启httpd服务,打开Windows7-1客户机就行验证
Service httpd restart

未分类

二、隐藏版本信息

1.打开抓包工具进行测试

未分类

2.到/etc/httpd.conf中开启default.conf

未分类

3.切换到/usr/local/httpd/conf/extra/目录中,编辑httpd-default.conf文件把serverToken Full改为serverToken prod。

未分类

未分类

4.重启httpd服务,进行验证。
service httpd restart

未分类

Vagrant 中安装 Mysql 如何从外边链接

在 Vagrant 中安装 Mysql 后从外部链接需要三步

  • 设置私有 ip
  • 去掉绑定 127.0.0.1
  • 对所有 ip 开放

设置私有 ip

修改 Vagrantfile 添加 private_network,这样外部可以通过该 ip 链接

config.vm.network "private_network", ip: "192.168.33.10"

这步需要放在第一步来完成,随后重新加载配置

$ vagrant reload

去掉绑定 127.0.0.1

如果你是使用 rpm 来安装的话,修改 /etc/mysql/mysql.conf.d/mysql.cnf,将绑定 127.0.0.1 这一行注释掉

# bind-address            = 127.0.0.1

对所有 ip 开放

登陆 Mysql 并对所有外网 ip 开放权限

$ mysql -uroot -p
> GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY 'your_mysql_password' WITH GRANT OPTION;

这样从外部通过如下命令就可以访问了

$ mysql -uroot -p -h 192.168.33.10

注意

如果在生产环境上的话建议只对指定 ip 开放权限

> GRANT ALL PRIVILEGES ON *.* TO 'root'@'213.1.2.1' IDENTIFIED BY 'your_mysql_password' WITH GRANT OPTION;

亲测Mysql表结构为InnoDB类型从ibd文件恢复数据

客户的机器系统异常关机,重启后mysql数据库不能正常启动,重装系统后发现数据库文件损坏,悲催的是客户数据库没有进行及时备份,只能想办法从数据库文件当中恢复,查找资料,试验各种方法,确认下面步骤可行:

一、找回表结构,如果表结构没有丢失直接到下一步

a、先创建一个数据库,这个数据库必须是没有表和任何操作的。
b、创建一个表结构,和要恢复的表名是一样的。表里的字段无所谓。一定要是innodb引擎的。CREATE TABLE `test`(  `testID` bigint(20)) ENGINE=InnoDB DEFAULT CHARSET=utf8;
c、关闭mysql, service mysqld stop;
d、用需要恢复的frm文件覆盖刚新建的frm文件;  
e、修改my.ini 里  innodb_force_recovery=1 , 如果不成修改为 2,3,4,5,6。
f、 启动mysql,service mysqld start;show create table test就能够看到表结构信息了。

二、找回数据

a、建立一个数据库,根据上面导出的创建表的sql执行创建表。
b、找到记录点。先要把当前数据库的表空间废弃掉,使当前ibd的数据文件和frm分离。  ALTER TABLE test DISCARD TABLESPACE;
c、把之前要恢复的 .ibd文件复制到新的表结构文件夹下。 使当前的ibd 和frm发生关系。ALTER TABLE test  IMPORT TABLESPACE;

d、将恢复好的数据导出就行了

InnoDB锁原理

背景

MySQL是一个支持插件式存储引擎的数据库系统,其中InnoDB是MySQL的事务安全的存储引擎,在OLTP系统中使用非常广乏。InnoDB最大的特性是支持事务,事务的特性包括原子性、一致性、隔离性、持久性,其中事务的隔离性,就是通过锁来实现的。在正式介绍锁之前,先来回顾一下MySql/InnoDB的隔离级别:

  • READ UNCOMMITTED 可以读取到未提交的数据,会产生脏读的问题
  • READ COMMITTED 读取已经提交的数据,不会有脏读,但是会有不可重复读和幻读。
  • REPEATABLE READ 在同一个事务中,可以重复读取,同时InnoDB在此隔离级别下不会有幻象读现象。
  • SERIALIZABLE 读取和写入都需要加锁,效率比较低。

锁的类型

InnoDB存储引擎有两种行级锁

  • 共享锁 S,允许事务读取一行数据。
  • 排他锁 X,允许事务修改一行数据。

他们的兼容性如下:

未分类

除此之外,InnoDB还有一种表级别锁,意向锁:

  • 意向共享锁 IS,表示事务想要获取表中某几行的共享锁。
  • 意向排他锁 IX,表示事务想要获取表中某几行的排他锁。

InnoDB的意向锁主要用户多粒度的锁并存的情况。比如事务A要在一个表上加S锁,如果表中的一行已被事务B加了X锁,那么该锁的申请也应被阻塞。如果表中的数据很多,逐行检查锁标志的开销将很大,系统的性能将会受到影响。为了解决这个问题,可以在表级上引入新的锁类型来表示其所属行的加锁情况,这就引出了“意向锁”的概念。举个例子,如果表中记录1亿,事务A把其中有几条记录上了行锁了,这时事务B需要给这个表加表级锁,如果没有意向锁的话,那就要去表中查找这一亿条记录是否上锁了。如果存在意向锁,那么假如事务A在更新一条记录之前,先加意向锁,再加X锁,事务B先检查该表上是否存在意向锁,存在的意向锁是否与自己准备加的锁冲突,如果有冲突,则等待直到事务A释放,而无须逐条记录去检测。事务B更新表时,其实无须知道到底哪一行被锁了,它只要知道反正有一行被锁了就行了。

说白了意向锁的主要作用是处理行锁和表锁之间的矛盾,能够显示“某个事务正在某一行上持有了锁,或者准备去持有锁”。

读取

innoDB中的数据读取分为锁定读取和非锁定读取。

非锁定读取

非锁定读指的是在读取的时候不需要加任何锁,读写不冲突。对于简单的查询语句select * from table where ?;在离级别READ UNCOMMITTED,READ COMMITTED和REPEATABLE READ下,是非锁定读取。在隔离级别SERIALIZABLE下,是锁定读取,需要获取行锁,此隔离级别效率极低,线上都不会采用。

在读多写少的OLTP系统当中,非锁定读取可以极大提高系统的并发处理能力。innoDB通过多版本的并发控制协议——MVCC (Multi-Version Concurrency Control)来实现非锁定读取,在读取的时候不用等待行上的锁释放,直接去读取行的一个快照数据。流程如下图所示:

未分类

对于行上的快照数据,innoDB是通过undo log来实现的,undo log可用于回滚事务,也可以用来实现MVCC功能。一个行上可能不只有一个版本的快照数据,对于事务隔离级别READ COMMITTED和REPEATABLE READ,他们所读取的快照版本是不一样的。在READ COMMITTED下,读取的快照数据总是最新的版本,在REPEATABLE READ下,总是读取事务开始时的行数据。

锁定读取

在某些情况下,为了保证数据的一致性,要先获取行锁,再进行数据读取。如下所示语句都会产生锁定读取:

  • select … lock in share mode; S
  • select … for update; X
  • insert into table values (..); 由于插入时需要唯一性检查,所以需要X锁。
  • update table set ? where ?; X
  • delete from table where ?; X

行锁的算法

  • Record Lock:单个行记录上锁。
  • Gap Lock:间隙锁,锁定一个范围,不包含记录本身。
  • Next-Key Lock:Gap Lock + Record Lock,锁定一个范围,并且锁定记录本身。

Record lock单条索引记录上加锁,Record lock锁住的永远是索引,而非记录本身。索引分为主键索引和非主键索引两种,如果一条sql语句操作了主键索引,MySQL就会锁定这条主键索引;如果一条语句操作了非主键索引,MySQL会先锁定该非主键索引,再锁定相关的主键索引。即使该表上没有任何索引,那么innodb会在后台创建一个隐藏的聚集主键索引,那么锁住的就是这个隐藏的聚集主键索引。所以说当一条sql没有走任何索引时,那么将会在每一条聚集索引后面加X锁。

Gap Lock锁定的是索引之间的间隙,并不是记录本身。例如有一个索引有3,5,6,10和20这几个值,他们之前的间隙包括(-∞,3)、(3,5)、(5,6)、(10,20)、(20,+∞),对于Gap Lock锁定就是这几个范围。

InnoDB在不同的隔离级别下使用的锁算法也不同。

READ COMMITTED

对于innoDB的READ COMMITTED隔离级别下,会存在幻象读问题。在该隔离级别下,除了外键约束和唯一性检查依然需要Gap Lock,其余情况均使用Record Lock进行锁定。

REPEATABLE READ

InnoDB在该隔离级别下,没有幻读现象。幻读是指在同一事务下,连续执行两次同样的SQL语句可能导致不同的结果,第二次执行可能返回之前不存在的行。例如对下述语句:select * from table where id > 10 for update; 在事务1查询之后,如果另一个事务2可以插入id大于10的记录,在事务1下次查询的时候也会返回事务2插入的记录,两次的读取结果不一样,这就是幻读。

InnoDB通过Next-key Lock来避免幻读的现象,除了锁住记录本身之外,还要锁住可能涉及到的间隙(Gap)。对于上述例子select * from table where id > 10 for update; 在REPEATABLE READ隔离级别下锁定是(10,+∞)这个范围。

总结

本文主要介绍了InnoDB的锁的类型及RC和RR级别下的加锁情况,希望能给大家带来帮助。

docker-compose运行jenkins集成sonarqube代码质量检测

  • 192.168.0.141 jenkins

  • 192.168.0.142 sonarqube

本实例用到2台机器,一台机器里面跑着jenkins服务,一台机器为sonarqube服务

jenkins服务部署这里就不做多的讲解,可以采用docker-compose部署,也可以单独部署,博主采用docker-compose部署的jenkins,我们在另外一台机器部署一个sonarqube服务,这里我们用mysql5.6的镜像和sonarqube的最新版的镜像镜像compose编排部署。

这里编排的是3个服务在一起了,有需要可以自行进行拆分

请参考博主开源项目:https://github.com/lizibin/docker-jenkins-sonarqube

1、拉取博主的项目,即可成功运行jenkins和sonarqube的代码质量检测

我们可以访问主机ip+9000端口访问到页面

未分类

2、这里我们还需要再jenkins进行配置一下插件,才可以自动分析代码质量

未分类

找到这个插件安装好这个插件就开始配置

3、配置jenkins 进入系统配置

未分类

继续配置Global Tool Configuration

未分类

然后我们就可以建立一个自由风格的项目来分析代码了

4、创建项目,需要勾上

未分类

未分类

修改Analysis properties

未分类

就可以开始构建了

5、构建完毕我们可以去9000端口查看页面详情

未分类

使用 Jenkins 自动化发布 PHP 项目

什么是 Jenkins

Jenkins是一个开源软件项目,是基于Java开发的一种持续集成工具,用于监控持续重复的工作,旨在提供一个开放易用的软件平台,使软件的持续集成变成可能。

使用 Docker 安装 Jenkins

避免装环境的折腾, 直接使用 docker-compose 安装,具体如何安装在 Docker 快速搭建 LNMP 环境 已经描述了

docker-compose.yml

jenkins:
  image: jenkins:latest
  ports:
    - "8080:8080"
  volumes:
      - ./jenkins:/var/jenkins_home:rw

注意:volumes 配置 jenkins 目录映射到本地

docker-compose up -d 

# 等待下载镜像,创建容器
Creating dnmp_jenkins_1    ... done
# 安装就这么简单

访问 8080 端口,进入初始化页面

访问: http://localhost:8080/

首次打开,需要输入秘钥,根据提示,可以在对应的目录 /jenkins/secrets 找到该文件
设置登录用户名密码后,进入几分钟的初始化过程…

未分类

配置自动化构建发布

配置远程服务器 SSH

菜单 -> 系统管理 -> 系统设置 -> SSH Servers

未分类

如图,是我配置的内容

未分类

Remote Directory 这个配置很关键,表示构建时的相对目录。这里我配置 “/“
配置完成后,最好 Test Configuration , 返回 Success 就表示成功!

新建发布项目

填写项目名称如, test
并选择项目类型,这里我选择”自由风格项目”

未分类

General

选择对应项目路径,我使用 GitHub project

未分类

源码管理

使用 git 源码仓库管理

未分类

构建

这里是最关键的,你可以打包源码发布到对应的服务器之上

未分类

  • Source files 表示打包好的源文件
  • Remote directory 表示你需要将源文件上传的远程路径(这个路径相对于 SSH 配置目录)
  • Exec command 上传完成后,执行的命令( hexo g 这个是我发布博客时的构建命令)

立即构建

选择对应的项目,点击立即构建

未分类

在构建执行状态中,可以点击 console output 看到构建的过程信息

未分类

使用 Supervisor 管理服务器后台进程

Supervisor (http://supervisord.org) 是一个用 Python 写的进程管理工具,可以很方便的用来启动、重启、关闭进程(不仅仅是 Python 进程)。除了对单个进程的控制,还可以同时启动、关闭多个进程,比如很不幸的服务器出问题导致所有应用程序都被杀死,此时可以用 supervisor 同时启动所有应用程序而不是一个一个地敲命令启动。

之所以使用 Supervisor,是因为服务器的 MongoDB 进程偶尔会 Crash,需要确保让它在挂掉后自动重启确保服务正常。

0x1 安装

直接使用 pip 进行安装:

$ sudo pip install supervisor

# 可能你会收到类似的报错:Supervisor requires Python 2.4 or later but does not work on any version of Python 3.  You are using version 3.4.3 (default, Oct 28 2017, 20:59:04)
# 可以手动安装新版 Supervisor,它支持 Python3:

$ pip install git+https://github.com/Supervisor/supervisor

# 设置环境变量:
$ vim ~/.bash_profile

在后面补充: PATH=$PATH:$HOME/bin:/usr/local/python/bin

$ source ~/.bash_profile

0x2 配置

$ echo_supervisord_conf > /etc/supervisord.conf

打开配置文件:

$ vim /etc/supervisord.conf

[unix_http_server]
file=/tmp/supervisor.sock   ; UNIX socket 文件,supervisorctl 会使用
;chmod=0700                 ; socket 文件的 mode,默认是 0700
;chown=nobody:nogroup       ; socket 文件的 owner,格式: uid:gid
;username=user              ; default is no username (open server)
;password=123               ; default is no password (open server)

;[inet_http_server]         ; HTTP 服务器,提供 web 管理界面
;port=127.0.0.1:9001        ; Web 管理后台运行的 IP 和端口,如果开放到公网,需要注意安全性
;username=user              ; 登录管理后台的用户名
;password=123               ; 登录管理后台的密码

[supervisord]
logfile=/tmp/supervisord.log ; 日志文件,默认是 $CWD/supervisord.log
logfile_maxbytes=50MB        ; 日志文件大小,超出会 rotate,默认 50MB
logfile_backups=10           ; 日志文件保留备份数量默认 10
loglevel=info                ; 日志级别,默认 info,其它: debug,warn,trace
pidfile=/tmp/supervisord.pid ; pid 文件
nodaemon=false               ; 是否在前台启动,默认是 false,即以 daemon 的方式启动
minfds=1024                  ; 可以打开的文件描述符的最小值,默认 1024
minprocs=200                 ; 可以打开的进程数的最小值,默认 200

; the below section must remain in the config file for RPC
; (supervisorctl/web interface) to work, additional interfaces may be
; added by defining them in separate rpcinterface: sections
[rpcinterface:supervisor]
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface

[supervisorctl]
serverurl=unix:///tmp/supervisor.sock ; 通过 UNIX socket 连接 supervisord,路径与 unix_http_server 部分的 file 一致
;serverurl=http://127.0.0.1:9001 ; 通过 HTTP 的方式连接 supervisord

;包含其他的配置文件
[include]
files = relative/directory/*.ini    ; 可以是 *.conf 或 *.ini

0x3 配置需要管理的进程

这里以添加 MongoDB 进程为例,首先修改 supervisord.conf:

$ vim supervisord.conf

# 找到最后一行,并取消注释和添加:
[include]
files = /etc/supervisor/*.conf

$ mkdir /etc/supervisor
$ cd /etc/suervisor
$ vim mongodb.conf

# 填入以下内容:

[program:mongodb]
command =  /usr/bin/mongod -port 27017 --dbpath /vr/lib/mongo
autostart = true     ; 在 supervisord 启动的时候也自动启动
startsecs = 5        ; 启动 5 秒后没有异常退出,就当作已经正常启动了
autorestart = true   ; 程序异常退出后自动重启
startretries = 3     ; 启动失败自动重试次数,默认是 3

0x4 启动 Supervisor

Supervisor 有两个主要的组成部分:

  1. supervisord,运行 Supervisor 时会启动一个进程 supervisord,它负责启动所管理的进程,并将所管理的进程作为自己的子进程来启动,而且可以在所管理的进程出现崩溃时自动重启。

  2. supervisorctl,是命令行管理工具,可以用来执行 stop、start、restart 等命令,来对这些子进程进行管理。

$ supervisord -c /etc/supervisord.conf
$ supervisorctl -c /etc/supervisord.conf status

> mongodb       RUNNING   pid 2366, uptime 0:01:00

未分类

0x5 可视化管理进程

$ vim /etc/supervisord.conf

# 取消注释和更改设置
[inet_http_server]         ; HTTP 服务器,提供 web 管理界面
port=0.0.0.0:8080          ; Web 管理后台运行的 IP 和端口,如果开放到公网,需要注意安全性
username=user              ; 登录管理后台的用户名
password=123               ; 登录管理后台的密码

[rpcinterface:supervisor]
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface

[supervisorctl]
serverurl=http://0.0.0.0:8080    ; 通过 HTTP 的方式连接 supervisord

通过浏览器打开网站 url:8080,输入帐号密码,可以在网页中查看进程:

未分类

0x6 开机自启动 Supervisor

Linux 在启动的时候会执行 /etc/rc.local 里面的脚本,所以只要在这里添加执行命令就可以:

# 如果是 Centos 添加以下内容
/usr/bin/supervisord -c /etc/supervisord.conf

# 以上内容需要添加在 exit 命令前,而且由于在执行 rc.local 脚本时,PATH 环境变量
# 未全部初始化,因此命令需要使用绝对路径。可以使用一下命令查看绝对路径:

$ sudo find / -name supervisord
> /usr/local/python/bin/supervisord

所以要改下路径:
/usr/local/python/bin/supervisord -c /etc/supervisord.conf

0x7 常见问题

在启动 supervisorctl 的时候可能会接受到 refuse connection 的报错,解决办法:

# 找到 supervisor.sock 的地址
$ find / -name supervisor.sock

# unlink 掉它,*** 换成真实地址
$ unlink /***/supervisor.sock

还遇到了另外一个问题,在 supervisor 运行一段时间后,web 端会访问不了,在后台企图通过 supervisorctl -c /etc/supervisord.conf 登录,发现还是报 refuse connection 的错误,还有

Error: Another program is already listening on a port that one of our HTTP servers is configured to use.  Shut this program down first before starting supervisord. 

尝试将[supervisorctl] 里面的属性 serverurl 修改成 unix 前缀,如 unix:///tmp/supervisord.sock,过一段时间再做观察。

python(sqlalchemy基本使用)

下面就开始让你见证orm的nb之处,盘古开天劈地之前,我们创建一个表是这样的

CREATE TABLE user (
    id INTEGER NOT NULL AUTO_INCREMENT,
    name VARCHAR(32),
    password VARCHAR(64),
    PRIMARY KEY (id)
)

这只是最简单的sql表,如果再加上外键关联什么的,一般程序员的脑容量是记不住那些sql语句的,于是有了orm,实现上面同样的功能,代码如下

import sqlalchemy
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String
engine = create_engine("mysql+pymysql://root:alex3714@localhost/testdb",
                                    encoding='utf-8', echo=True)
Base = declarative_base() #生成orm基类
class User(Base):
    __tablename__ = 'user' #表名
    id = Column(Integer, primary_key=True)
    name = Column(String(32))
    password = Column(String(64))
Base.metadata.create_all(engine) #创建表结构

你说,娘那个腚的,并没有感觉代码量变少啊,呵呵, 孩子莫猴急,好戏在后面

Lazy Connecting
The Engine, when first returned by create_engine(), has not actually tried to connect to the database yet; that happens only the first time it is asked to perform a task against the database.

除上面的创建之外,还有一种创建表的方式,虽不常用,但还是看看吧

http://www.cnblogs.com/alex3714/articles/5978329.html#

事实上,我们用第一种方式创建的表就是基于第2种方式的再封装。

最基本的表我们创建好了,那我们开始用orm创建一条数据试试

Session_class = sessionmaker(bind=engine) #创建与数据库的会话session class ,注意,这里返回给session的是个class,不是实例
Session = Session_class() #生成session实例
user_obj = User(name="alex",password="alex3714") #生成你要创建的数据对象
print(user_obj.name,user_obj.id)  #此时还没创建对象呢,不信你打印一下id发现还是None
Session.add(user_obj) #把要创建的数据对象添加到这个session里, 一会统一创建
print(user_obj.name,user_obj.id) #此时也依然还没创建
Session.commit() #现此才统一提交,创建数据

我擦,写这么多代码才创建一条数据,你表示太tm的费劲了,正要转身离开,我拉住你的手不放开,高潮还没到。。

查询

my_user = Session.query(User).filter_by(name="alex").first()
print(my_user)

此时你看到的输出是这样的应该

<__main__.User object at 0x105b4ba90>

我擦,这是什么?这就是你要的数据呀, 只不过sqlalchemy帮你把返回的数据映射成一个对象啦,这样你调用每个字段就可以跟调用对象属性一样啦,like this..

print(my_user.id,my_user.name,my_user.password)
输出
1 alex alex3714

不过刚才上面的显示的内存对象对址你是没办法分清返回的是什么数据的,除非打印具体字段看一下,如果想让它变的可读,只需在定义表的类下面加上这样的代码

def __repr__(self):
    return "<User(name='%s',  password='%s')>" % (
        self.name, self.password)

修改

my_user = Session.query(User).filter_by(name="alex").first()
my_user.name = "Alex Li"
Session.commit()

回滚

my_user = Session.query(User).filter_by(id=1).first()
my_user.name = "Jack"
fake_user = User(name='Rain', password='12345')
Session.add(fake_user)
print(Session.query(User).filter(User.name.in_(['Jack','rain'])).all() )  #这时看session里有你刚添加和修改的数据
Session.rollback() #此时你rollback一下
print(Session.query(User).filter(User.name.in_(['Jack','rain'])).all() ) #再查就发现刚才添加的数据没有了。
# Session
# Session.commit()

获取所有数据

print(Session.query(User.name,User.id).all() )

多条件查询

objs = Session.query(User).filter(User.id>0).filter(User.id<7).all()

上面2个filter的关系相当于 user.id >1 AND user.id <7 的效果

统计和分组

Session.query(User).filter(User.name.like("Ra%")).count()

分组

from sqlalchemy import func
print(Session.query(func.count(User.name),User.name).group_by(User.name).all() )

相当于原生sql为

http://www.cnblogs.com/alex3714/articles/5978329.html#

输出为

[(1, ‘Jack’), (2, ‘Rain’)]

外键关联

我们创建一个addresses表,跟user表关联

from sqlalchemy import ForeignKey
from sqlalchemy.orm import relationship
class Address(Base):
    __tablename__ = 'addresses'
    id = Column(Integer, primary_key=True)
    email_address = Column(String(32), nullable=False)
    user_id = Column(Integer, ForeignKey('user.id'))
    user = relationship("User", backref="addresses") #这个nb,允许你在user表里通过backref字段反向查出所有它在addresses表里的关联项
    def __repr__(self):
        return "<Address(email_address='%s')>" % self.email_address
The relationship.back_populates parameter is a newer version of a very common SQLAlchemy feature calledrelationship.backref. The relationship.backref parameter hasn’t gone anywhere and will always remain available! The relationship.back_populates is the same thing, except a little more verbose and easier to manipulate. For an overview of the entire topic, see the section Linking Relationships with Backref.

表创建好后,我们可以这样反查试试

obj = Session.query(User).first()
for i in obj.addresses: #通过user对象反查关联的addresses记录
    print(i)
addr_obj = Session.query(Address).first()
print(addr_obj.user.name)  #在addr_obj里直接查关联的user表

创建关联对象

obj = Session.query(User).filter(User.name=='rain').all()[0]
print(obj.addresses)
obj.addresses = [Address(email_address="[email protected]"), #添加关联对象
                 Address(email_address="[email protected]")]
Session.commit()

常用查询语法

Common Filter Operators

Here’s a rundown of some of the most common operators used in filter():

  • equals:
     query.filter(User.name == 'ed')
  • not equals:
     query.filter(User.name != 'ed')
  • LIKE:

query.filter(User.name.like(‘%ed%’))

  • IN:
  • NOT IN:
    query.filter(~User.name.in_([‘ed’, ‘wendy’, ‘jack’]))
  • IS NULL:
  • IS NOT NULL:
  • AND:
    2.1. ObjectRelationalTutorial 17
query.filter(User.name.in_(['ed', 'wendy', 'jack']))
# works with query objects too:

query.filter(User.name.in_( session.query(User.name).filter(User.name.like(‘%ed%’))

))

query.filter(User.name == None)
# alternatively, if pep8/linters are a concern
query.filter(User.name.is_(None))
query.filter(User.name != None)
# alternatively, if pep8/linters are a concern
query.filter(User.name.isnot(None))

SQLAlchemy Documentation, Release 1.1.0b1
# use and_()
from sqlalchemy import and_
query.filter(and_(User.name == ‘ed’, User.fullname == ‘Ed Jones’))

# or send multiple expressions to .filter()
query.filter(User.name == 'ed', User.fullname == 'Ed Jones')
# or chain multiple filter()/filter_by() calls
query.filter(User.name == 'ed').filter(User.fullname == 'Ed Jones')

Note: Makesureyouuseand_()andnotthePythonandoperator! • OR:

Note: Makesureyouuseor_()andnotthePythonoroperator! • MATCH:

query.filter(User.name.match(‘wendy’))
Note: match() uses a database-specific MATCH or CONTAINS f

原文:http://www.cnblogs.com/alex3714/articles/5978329.html

Flask—flask_mail邮箱发送

flask-mail 文档
http://www.pythondoc.com/flask-mail/index.html
项目中不可避免需要使用邮箱认证,如果使用flask则可以利用Flask-Mail来实现

开启qq邮箱SMTP服务

未分类

之后手机验证什么的依自己帐号设置,验证成功后会获得一个授权码,这个需要保存后续发送邮箱时密码就填这个授权码。

安装Flask-Mail

使用 pip 或者 easy_install 安装 Flask-Mail:

pip install Flask-Mail

或者从版本控制系统(github)中下载最新的版本:

git clone https://github.com/mattupstate/flask-mail.git
cd flask-mail
python setup.py install

如果你正在使用 virtualenv,假设你会安装 flask-mail 在运行你的 Flask 应用程序的同一个 virtualenv 上。

配置 Flask-Mail

Flask-Mail 使用标准的 Flask 配置 API 进行配置。下面这些是可用的配置型(每一个将会在文档中进行解释):

MAIL_SERVER : 默认为 ‘localhost’
MAIL_PORT : 默认为 25
MAIL_USE_TLS : 默认为 False
MAIL_USE_SSL : 默认为 False
MAIL_DEBUG : 默认为 app.debug
MAIL_USERNAME : 默认为 None
MAIL_PASSWORD : 默认为 None
MAIL_DEFAULT_SENDER : 默认为 None
MAIL_MAX_EMAILS : 默认为 None
MAIL_SUPPRESS_SEND : 默认为 app.testing
MAIL_ASCII_ATTACHMENTS : 默认为 False
另外,Flask-Mail 使用标准的 Flask 的 TESTING 配置项用于单元测试(下面会具体介绍)。
配置项 默认值 功能
MAIL_SERVER localhost   邮箱服务器
MAIL_PORT   25  端口
MAIL_USE_TLS    False   是否使用TLS
MAIL_USE_SSL    False   是否使用SSL
MAIL_DEBUG  app.debug   是否为DEBUG模式,打印调试消息
MAIL_SUPPRESS_SEND  app.testing 设置是否真的发送邮件,True不发送
MAIL_USERNAME   None    用户名,填邮箱
MAIL_PASSWORD   None    密码,填授权码
MAIL_DEFAULT_SENDER None    默认发送者,填邮箱
MAIL_MAX_EMAILS None    一次连接中的发送邮件的上限
MAIL_ASCII_ATTACHMENTS  False   如果 MAIL_ASCII_ATTACHMENTS 设置成 True 的话,文件名将会转换成 ASCII 的。一般用于添加附件。

大量邮件

通常在一个 Web 应用中每一个请求会同时发送一封或者两封邮件。在某些特定的场景下,有可能会发送数十或者数百封邮件,不过这种发送工作会给交离线任务或者脚本执行。

在这种情况下发送邮件的代码会有些不同:

with mail.connect() as conn:
    for user in users:
        message = '...'
        subject = "hello, %s" % user.name
        msg = Message(recipients=[user.email],
                      body=message,
                      subject=subject)

        conn.send(msg)

与电子邮件服务器的连接会一直保持活动状态直到所有的邮件都已经发送完成后才会关闭(断开)。

有些邮件服务器会限制一次连接中的发送邮件的上限。你可以设置重连前的发送邮件的最大数,通过配置 MAIL_MAX_EMAILS 。

附件

with app.open_resource("image.png") as fp:
    msg.attach("image.png", "image/png", fp.read())

完整例子

from flask import Flask, request
from flask_script import Manager, Shell
from flask_mail import Mail, Message
from threading import Thread


app = Flask(__name__)
app.config['MAIL_DEBUG'] = True             # 开启debug,便于调试看信息
app.config['MAIL_SUPPRESS_SEND'] = False    # 发送邮件,为True则不发送
app.config['MAIL_SERVER'] = 'smtp.qq.com'   # 邮箱服务器
app.config['MAIL_PORT'] = 465               # 端口
app.config['MAIL_USE_SSL'] = True           # 重要,qq邮箱需要使用SSL
app.config['MAIL_USE_TLS'] = False          # 不需要使用TLS
app.config['MAIL_USERNAME'] = '[email protected]'  # 填邮箱
app.config['MAIL_PASSWORD'] = 'Wyf880204*+'      # 填授权码
app.config['MAIL_DEFAULT_SENDER'] = '[email protected]'  # 填邮箱,默认发送者
manager = Manager(app)
mail = Mail(app)


# 异步发送邮件
def send_async_email(app, msg):
    with app.app_context():
        mail.send(msg)


@app.route('/')
def index():
    msg = Message(subject='Hello World',
                  sender="[email protected]",  # 需要使用默认发送者则不用填
                  recipients=['[email protected]'])
    # 邮件内容会以文本和html两种格式呈现,而你能看到哪种格式取决于你的邮件客户端。
    msg.body = 'sended by flask-email'
    msg.html = '<b>测试Flask发送邮件<b>'
    thread = Thread(target=send_async_email, args=[app, msg])
    thread.start()
    return '<h1>邮件发送成功</h1>'


if __name__ == '__main__':
    manager.run()

效果图:

未分类