Day 2 -

编写数据库模块

在一个

Web App

中,所有数据,包括用户信息、发布的日志、评论等,都存储在数据库中。在

awesome-python-app

中,我们选择

MySQL

作为数据库。

Web App

里面有很多地方都要访问数据库。访问数据库需要创建数据库连接、游标对象,然后执行

SQL

语句,最后处理异常,清理资源。这些访问数据库的代码如果分散到各个函数中,势必无法维护,也不利于代码复用。

此外,在一个

Web App

中,有多个用户会同时访问,系统以多进程或多线程模式来处理每个用户的请求。假设以多线程为例,每个线程在访问数据库时,都必须创建仅属于自身的连接,对别的线程不可见,否则,就会造成数据库操作混乱。

所以,我们还要创建一个简单可靠的数据库访问模型,在一个线程中,能既安全又简单地操作数据库。

为什么不选择

SQLAlchemy

SQLAlchemy

太庞大,过度地面向对象设计导致

API

太复杂。

所以我们决定自己设计一个封装基本的

SELECT

INSERT

UPDATE

DELETE

操作的

db

模块:

transwarp.db

设计db接口

设计底层模块的原则是,根据上层调用者设计简单易用的

API

接口,然后,实现模块内部代码。

假设

transwarp.db

模块已经编写完毕,我们希望以这样的方式来调用它:

首先,初始化数据库连接信息,通过

create_engine()

函数:

from

transwarp import

db

db.create_engine(user='root', password='password', database='test', host='127.0.0.1', port=3306)

然后,就可以直接操作

SQL

了。

如果需要做一个查询,可以直接调用

select()

方法,返回的是

list

,每一个元素是用

dict

表示的对应的行:

users = db.select('select * from user')

# users =>

# [

#     { "id": 1, "name": "Michael"},

#     { "id": 2, "name": "Bob"},

#     { "id": 3, "name": "Adam"}

# ]

如果要执行

INSERT

UPDATE

DELETE

操作,执行

update()

方法,返回受影响的行数:

n = db.update

('insert into user(id, name) values(?, ?)', 4, 'Jack')

update()

函数签名为:

update

(sql

, *args)

统一用

?

作为占位符,并传入可变参数来绑定,从根本上避免

SQL

注入攻击

每个

select()

update()

调用,都隐含地自动打开并关闭了数据库连接,这样,上层调用者就完全不必关心数据库底层连接。

但是,如果要在一个数据库连接里执行多个

SQL

语句怎么办?我们用一个

with

语句实现:

with db.connection():

db.select

('...')

db.update

('...')

db.update

('...')

如果要在一个数据库事务中执行多个

SQL

语句怎么办?我们还是用一个

with

语句实现:

with db.transaction():

db.select

('...')

db.update

('...')

db.update

('...')

实现db模块

由于模块是全局对象,模块变量是全局唯一变量,所以,有两个重要的模块变量:

# db.py

# 数据库引擎对象:

class _Engine(object):

def

__init__

(self, connect):

self._connect = connect

def

connect

(self):

return

self._connect()

engine = None

# 持有数据库连接的上下文对象:

class _DbCtx(threading.local):

def

__init__

(self):

self.connection = None

self.transactions = 0

def

is_init

(self):

return

not

self.connection is

None

def

init

(self):

self.connection = _LasyConnection()

self.transactions = 0

def

cleanup

(self):

self.connection.cleanup()

self.connection = None

def

cursor

(self):

return

self.connection.cursor()

_db_ctx = _DbCtx()

由于

_db_ctx

threadlocal

对象,所以,它持有的数据库连接对于每个线程看到的都是不一样的。任何一个线程都无法访问到其他线程持有的数据库连接。

有了这两个全局变量,我们继续实现数据库连接的上下文,目的是自动获取和释放连接:

class _ConnectionCtx(object):

def

__enter__

(self):

global

_db_ctx

self.should_cleanup = False

if

not

_db_ctx.is_init():

_db_ctx.init()

self.should_cleanup = True

return

self

def

__exit__

(self, exctype, excvalue, traceback):

global

_db_ctx

if

self.should_cleanup:

_db_ctx.cleanup()

def

connection

():

return

_ConnectionCtx()

定义了

__enter__()

__exit__()

的对象可以用于

with

语句,确保任何情况下

__exit__()

方法可以被调用。

_ConnectionCtx

的作用域作用到一个函数调用上,可以这么写:

with

connection():

do_some_db_operation()

但是更简单的写法是写个

@decorator

@with_connection

def

do_some_db_operation

():

pass

这样,我们实现

select()

update()

方法就更简单了:

@with_connection

def

select

(sql, *args):

pass

@with_connection

def

update

(sql, *args):

pass

注意到

Connection

对象是存储在

_DbCtx

这个

threadlocal

对象里的,因此,嵌套使用

with connection()

也没有问题。

_DbCtx

永远检测当前是否已存在

Connection

,如果存在,直接使用,如果不存在,则打开一个新的

Connection

对于

transaction

也是类似的,

with transaction()

定义了一个数据库事务:

with db.transaction():

db.select

('...')

db.update

('...')

db.update

('...')

函数作用域的事务也有一个简化的

@decorator

@with_transaction

def

do_in_transaction

():

pass

事务也可以嵌套,内层事务会自动合并到外层事务中,这种事务模型足够满足

99%

的需求。

事务嵌套比

Connection

嵌套复杂一点,因为事务嵌套需要计数,每遇到一层嵌套就

+1

,离开一层嵌套就

-1

,最后到

0

时提交事务:

class _TransactionCtx(object):

def

__enter__

(self):

global

_db_ctx

self.should_close_conn = False

if

not

_db_ctx.is_init():

_db_ctx.init()

self.should_close_conn = True

_db_ctx.transactions = _db_ctx.transactions + 1

return

self

def

__exit__

(self, exctype, excvalue, traceback):

global

_db_ctx

_db_ctx.transactions = _db_ctx.transactions - 1

try

:

if

_db_ctx.transactions==0:

if

exctype is

None:

self.commit()

else

:

self.rollback()

finally

:

if

self.should_close_conn:

_db_ctx.cleanup()

def

commit

(self):

global

_db_ctx

try

:

_db_ctx.connection.commit()

except

:

_db_ctx.connection.rollback()

raise

def

rollback

(self):

global

_db_ctx

_db_ctx.connection.rollback()

初识python廖雪峰_廖雪峰Python教程[完整版]相关推荐

  1. python入门教程完整版(懂中文就能学会)-Python入门教程完整版(懂中文就能学会)...

    不过小编的内心是强大的,网友虐我千百遍,我待网友如初恋,因为今天又给大家带来了干货,Python入门教程完整版,完整版啊!完整版! 言归正传,小编该给大家介绍一下这套教程了,希望每个小伙伴都沉迷学习, ...

  2. python27-资源|全机器学习和Python的27个速查表(完整版)

    原标题:资源|全机器学习和Python的27个速查表(完整版) 机器学习(Machine Learning) 有不少有用的流程图和机器学习算法表. 这里只包括所发现的最全面的速查表. 神经网络架构(N ...

  3. python3入门与进阶笔记_16_变量进阶 — 黑马程序员《Python入门教程完整版》笔记...

    变量进阶(理解) - 黑马程序员<Python入门教程完整版>笔记 目标变量的引用 可变和不可变类型 局部变量和全局变量 01. 变量的引用变量 和 数据 都是保存在 内存 中的 在 Py ...

  4. python新手教程全套_Python入门教程完整版(懂中文就能学会)

    前几天给大家分享视频<python基础教程>受到了广泛的关注,有人不知道怎么领取,居然称小编为"骗子". 不过小编的内心是强大的,网友虐我千百遍,我待网友如初恋,因为今 ...

  5. Python入门教程完整版

    今天本宝宝给大家带来了干货,Python入门教程完整版,完整版啊!完整版! 言归正传,我来给大家介绍一下这套教程,希望每个小伙伴都沉迷学习,无法自拔! 本套教程学习时间15天 1-3天内容:为Linu ...

  6. python文件目录管理 项目_Python入门教程完整版(懂中文就能学会)

    今天给大家带来了干货,Python入门教程完整版,完整版啊!完整版! 言归正传,小编该给大家介绍一下这套教程了,希望每个小伙伴都沉迷学习,无法自拔 本套教程学习时间15天 1-3天内容:为Linux基 ...

  7. Python入门教程完整版(懂中文就能学会)

    今天本宝宝给大家带来了干货,Python入门教程完整版,完整版啊!完整版! 言归正传,我来给大家介绍一下这套教程,希望每个小伙伴都沉迷学习,无法自拔! 本套教程学习时间15天 1-3天内容:为Linu ...

  8. 最新版python教程_Python入门教程完整版(懂中文就能学会)

    前几天给大家分享视频<python基础教程>受到了广泛的关注,有人不知道怎么领取,居然称小编为"骗子". 不过小编的内心是强大的,网友虐我千百遍,我待网友如初恋,因为今 ...

  9. python设计游戏的背景_04_游戏背景 — 黑马程序员《Python入门教程完整版》笔记...

    游戏背景 黑马程序员<Python入门教程完整版>笔记 - 黑马程序员<Python入门教程完整版>笔记 目标背景交替滚动的思路确定 显示游戏背景 01. 背景交替滚动的思路确 ...

  10. 视频教程-大型Java项目视频教程_王勇老师DRP项目教程完整版292集-Java

    大型Java项目视频教程_王勇老师DRP项目教程完整版292集 动力节点王勇老师,CCTV<影响力对话>栏目特约嘉宾,Java培训知名讲师,中国Java培训领军人物,北京动力节点创始人,董 ...

最新文章

  1. Android13.9.15
  2. python怎么发送邮件_python中是如何借助smtp协议发送邮件的?
  3. mysql主从同步默认延迟_减少mysql主从数据同步延迟问题的详解
  4. IndexError: only integers, slices (`:`), ellipsis (`...`), numpy.newaxis (`None`) and integer or boo
  5. 关于Netty的ByteBuff内存泄漏问题
  6. Linux系统软件包的管理   3月30日课程
  7. linux链接时报未定义的引用,g ++链接或引用不与本地安装的库一起使用:未定义的引用...
  8. Linux CentOS 7 下 安装SimHei字体
  9. 左右手桌面股票盯盘软件(DesktopStockTracking)
  10. Javaweb支付宝支付
  11. nc文件处理学习资料
  12. 清华AMiner团队推出AI订阅:实时追踪科研动态,定制个人科研信息流 | 专访唐杰教授团队
  13. Win10下用SCP命令免密码上传、下载阿里云服务器上的文件
  14. Maven Archetype
  15. Java强、软、弱、虚四大引用(附代码示例)
  16. Win10QQ和QQ音乐可以正常使用,但网页无法打开,并报错DNS_PROBE_POSSIBLE的解决方法
  17. 基于微信小程序视频点播系统 视频点播小程序毕业设计 毕业论文 开题报告和效果图参考
  18. php如何调用163邮箱发邮件,ThinkPHP3.2利用QQ邮箱/163邮箱通过PHPMailer发送邮件的方法...
  19. 开发常用镜像资源替换为国内开源镜像(yum,compose,maven,docker,android sdk,npm,国内开源镜像汇总)...
  20. 【无标题】第一条博客

热门文章

  1. android viewflipper图片轮播,ViewFlipper探索与使用——顺便实现Android图片轮播
  2. Visio 2013打开自动关闭,闪退的解决办法
  3. java实训报告_Java实验报告三
  4. 重启打印机服务bat命令
  5. 人人商城生成app教程_人人商城打包app教程 方法 hbuilder打包支持支付宝微信原生支付...
  6. ajax 上传文件实例,Ajax 之文件上传
  7. python库下载地址
  8. python与c语言数据交互,python与c语言交互---学习012
  9. matlab生成点的坐标,根据点的发展坐标,将点的轨迹画出来
  10. 无线充电协议Qi 转 UART