在一个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_ctxthreadlocal对象,所以,它持有的数据库连接对于每个线程看到的都是不一样的。任何一个线程都无法访问到其他线程持有的数据库连接。

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

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()

python Day 2 - 编写数据库模块的更多相关文章

  1. python import自己编写的模块:import json和simplejson

    python import的模块都是库里面的,而自己的模块也可以添加 比如python的json处理,库里没有json文件,import json不行. 网上大部分人告诉你处理json用 dumps和 ...

  2. Python全栈 MongoDB 数据库(聚合、二进制、GridFS、pymongo模块)

    断网了2天  今天补上     聚合操作: 对文档的信息进行整理统计的操作 返回:统计后的文档集合 db.collection.aggregate() 功能:聚合函数,完成聚合操作 参数:聚合条件,配 ...

  3. Python安装MySQL数据库模块

    背景 折腾: [记录]使用Python操作MySQL数据库 的过程中,需要去安装MySQLdb. 下载MySQLdb 去官网: http://pypi.python.org/pypi/MySQL-py ...

  4. python学习笔记之常用模块(第五天)

    参考老师的博客: 金角:http://www.cnblogs.com/alex3714/articles/5161349.html 银角:http://www.cnblogs.com/wupeiqi/ ...

  5. 用Perl编写Apache模块续二 - SVN动态鉴权实现SVNAuth 禅道版

    代码地址:https://code.csdn.net/x3dcn/svnauth 以禅道项目管理系统的数据库结构为标准,实现了可用的svn authz验证功能. 以用户名.密码.项目的acl开发程度o ...

  6. Python之进程 2 - multiprocessing模块

    ​ 我们已经了解了,运行中的程序就是一个进程.所有的进程都是通过它的父进程来创建的.因此,运行起来的python程序也是一个进程,那么我们也可以在程序中再创建进程.多个进程可以实现并发效果,也就是说, ...

  7. {Python之线程} 一 背景知识 二 线程与进程的关系 三 线程的特点 四 线程的实际应用场景 五 内存中的线程 六 用户级线程和内核级线程(了解) 七 python与线程 八 Threading模块 九 锁 十 信号量 十一 事件Event 十二 条件Condition(了解) 十三 定时器

    Python之线程 线程 本节目录 一 背景知识 二 线程与进程的关系 三 线程的特点 四 线程的实际应用场景 五 内存中的线程 六 用户级线程和内核级线程(了解) 七 python与线程 八 Thr ...

  8. Python操作三大主流数据库☝☝☝

    Python操作三大主流数据库☝☝☝ Python 标准数据库接口为 Python DB-API,Python DB-API为开发人员提供了数据库应用编程接口. Python 数据库接口支持非常多的数 ...

  9. Python操作三大主流数据库✍✍✍

    Python操作三大主流数据库 Python 标准数据库接口为 Python DB-API,Python DB-API为开发人员提供了数据库应用编程接口. Python 数据库接口支持非常多的数据库, ...

随机推荐

  1. c# 实现文件批量压缩

    今天改一个网站的功能,网站提供一些微信的素材,每个页面对应一套素材,如果会员一张一张下载,那么网站交互性就有点太差了.所以修改的内容就是提供一个按钮,点击按钮将这套图片和网站信息进行打包下载. 思路: ...

  2. IE11的CSS兼容性问题

    最近测试给了我一大堆BUG,一瞅发现全是IE11的.吐槽一下这个浏览器真的比较特立独行.很多默认的样式跟别的浏览器不同,而且最明显的一点应该是padding左右内边距往往比别的浏览器大了一倍.但是当需 ...

  3. 『奇葩问题集锦』function * (next){ 执行报错 SyntaxError: Unexpected token *

    这是因为  app.use(function * (){ 语句中有一个 * ,这种方式被称为generator functions ,一般写作function *(){...} 的形式,在此类func ...

  4. 四句话总结JavaScript作用域

    上一篇文章中简单介绍了一下JS作用域,本篇将作进一步探究和总结. 前言:JavaScript的作用域一直以来都是前端开发中比较难以理解的知识点,JavaScript6中新引入了 let 关键字,用于指 ...

  5. KVO与KVC初步了解

    参考: http://magicalboy.com/kvc_and_kvo/ http://www.mamicode.com/info-detail-515516.html KVC,即是指 NSKey ...

  6. jMeter之二

    jMeter应用的最小子集有如下三个概念: 首先是线程组(Thread Group),线程组意味着定义一下多少个线程,多长时间建立起来(模拟增量按照一定频度上扬)以及循环多少次: 第二个是采样器(Sa ...

  7. UIVIewController自定义切换效果-b

      之前介绍动画时提过UIView的转场动画,但是开发中我们碰到更多的viewController的切换,ios中常见的viewcontroller切换有四种:模态视图,导航栏控制器,UITabBar ...

  8. 预处理命令#define #undef #if #endif 的基本用法

    C#的预处理命令其实还是蛮有用的,但是真正使用过得人不多,这个介绍一下平时用的比较多的预处理命令中的几个:#define,#undef ,#if,#endif.除此之外还有一些预处理命令#warnin ...

  9. 禁止form表单回车键进行提交

    使用EasyUI的时候,我们会给一个datagrid添加一个搜索栏,但是这个搜索栏的form,我们一般使用ajax向服务器提交数据,因此在这样一个环境下可以考虑禁止用户按回车键(Enter)提交.代码 ...

  10. 不同框架实现的WebService的服务端获取HttpServletRequest的方法

    一. 基于xfire实现的WebService HttpServletRequest request = XFireServletController.getRequest(); 二. 基于axis实 ...