《python核心编程》--读书笔记 第21章 数据库编程

准备:今天拿笔记本装了mysql,这样就能在不同地方用其他电脑远程访问同一个数据库了。

python安装MySQLdb模块:http://www.codegood.com/downloads

21.1 介绍

本章的主题是如何在大型项目中使用关系型数据库。本章对数据库知识和SQL语言不做介绍,本章从一个Python应用程序开始,了解在Python框架下,如何将数据保存到数据库,如何将数据从数据库中取出来。“在Python世界里,无需怀疑,与数据库协同工作已经几乎是所有应用程序的核心部分了。”作者原话。

所有Python接口程序都一定程度上遵守Python DB-API 规范。

21.2 Python数据库应用程序

什么是DB-API?DB-API是一个规范。它定义了一系列必须的对象和数据库存取方式,以便为各种各样的底层数据库系统和多种多样的数据库接口程序提供一致的访问接口。

下面是一个例子:

#-*- coding:utf-8 -*-

import MySQLdb as SQL

#首先需要与数据库连接
cxn = SQL.connect(host='192.168.1.105', #这里是自己数据库主机地址
                       user='xxx',     #用户名
                       passwd='xxxx', #密码
                       db='JiaoYanShi', #所用数据库名称
                       port=3306,       #mysql默认端口
                       charset='utf8')  #编码方式
#连接好以后与数据库进行交互需要创建游标对象,一个游标允许用户执行数据库命令和得到查询结果。
cur = cxn.cursor()
#创建一个表
cur.execute('CREATE TABLE IF NOT EXISTS users(login VARCHAR(8),uid INT)')
#插入数据
cur.execute("INSERT INTO users VALUES('john',7000)")
cur.execute("INSERT INTO users VALUES('jane',7001)")
cur.execute("INSERT INTO users VALUES('bob',7200)")
#执行execute以后数据已经进入了数据库,但是如果没有下面的事务提交语句,已经进入的数据就会回滚,导致数据库中不会有数据
# python 操作mysql 是用 事物 的方式来实现的,所以必须有commit 提交的过程,否则数据表不会生效
#然而commit原理还没怎么懂…这样commit()不知道是否妥当,后续继续研究
cxn.commit()

#必须进行select以后才会有打印结果
cur.execute("SELECT * FROM users")
content = cur.fetchall()
for data in content:
    print data
print '-'*50

#下面进行表的更新
cur.execute("UPDATE users SET uid = 7100 WHERE uid = 7001 ")
cxn.commit()

cur.execute("SELECT * FROM users")
content = cur.fetchall()
for data in content:
    print data
print '-'*50
 
#下面进行表数据删除
cur.execute("DELETE FROM users WHERE login = 'bob'")
cxn.commit()

cur.execute("SELECT * FROM users")
content = cur.fetchall()
for data in content:
    print data

#关闭游标对象
cur.close()
#关闭连接
cxn.close()

>>>
(u'john', 7000L)
(u'jane', 7001L)
(u'bob', 7200L)
--------------------------------------------------
(u'john', 7000L)
(u'jane', 7100L)
(u'bob', 7200L)
--------------------------------------------------
(u'john', 7000L)
(u'jane', 7100L)
[Finished in 0.3s]

下面的这个例子展示了数据库数据通讯的一般框架:

#-*- coding:utf-8 -*-

import os
from random import randrange as rrange

COLSIZ = 10
RDBMSs = {'s': 'sqlite', 'm': 'mysql', 'g': 'gadfly'}
DB_EXC = None

HOST='xxx.xxx.xxx.xxx' #这里是HOST地址
User='xxxx'            #用户名
Passwd='xxxxxx'        #密码
Port=3306              #mysql默认端口
Charset='utf8'         #编码方式

#下面这个函数是选择数据库类型
def setup():
    return RDBMSs[raw_input('''
Choose a database system:

(M)ySQL
(G)adfly
(S)QLite

Enter choice: ''').strip().lower()[0]]

#下面是返回一个database.connect连接对象
def connect(db, dbName):
    global DB_EXC
    dbDir = '%s_%s' % (db, dbName)

    if db == 'sqlite':
        try:
            import sqlite3
        except ImportError, e:
            try:
                from pysqlite2 import dbapi2 as sqlite3
            except ImportError, e:
                return None

        DB_EXC = sqlite3
        if not os.path.isdir(dbDir):
            os.mkdir(dbDir)
        cxn = sqlite3.connect(os.path.join(dbDir, dbName))

    elif db == 'mysql':
        try:
            import MySQLdb
            #下面这个模块是专门用来进行数据库错误处理的
            import _mysql_exceptions as DB_EXC
        except ImportError, e:
            return None
        #下面的处理时用来创建数据库(如果不存在),可以作为一个标准处理流程
        try:
            cxn = MySQLdb.connect(host = HOST,
                                        user = User,
                                        passwd = Passwd,
                                        db=dbName,
                                        port = Port,
                                        charset='utf8')
        #连接不成功进行如下处理
        except DB_EXC.OperationalError, e:
            #先连接一下数据库地址,不写数据库名称
            cxn = MySQLdb.connect(host = HOST,
                                        user = User,
                                        passwd = Passwd,
                                        port = Port,
                                        charset='utf8')
            try:
                cxn.query('DROP DATABASE %s' % dbName)
            except DB_EXC.OperationalError, e:
                pass
            #创建数据库
            cxn.query('CREATE DATABASE %s' % dbName)
            cxn.commit()
            cxn.close()
            #连接上数据库
            cxn = MySQLdb.connect(host = HOST,
                                        user = User,
                                        passwd = Passwd,
                                        db = dbName,
                                        port = Port,
                                        charset='utf8')

    elif db == 'gadfly':
        try:
            from gadfly import gadfly
            DB_EXC = gadfly
        except ImportError, e:
            return None

        try:
            cxn = gadfly(dbName, dbDir)
        except IOError, e:
            cxn = gadfly()
            if not os.path.isdir(dbDir):
                os.mkdir(dbDir)
            cxn.startup(dbName, dbDir)
    else:
        return None
    return cxn

drop = lambda cur: cur.execute('DROP TABLE users')

def create(cur):
    #创建新的表users
    try:
        cur.execute('''
            CREATE TABLE users (
                login VARCHAR(8),
                uid INTEGER,
                prid INTEGER)
        ''')
    except DB_EXC.OperationalError, e:
        drop(cur)
        create(cur)

#插入的数据
NAMES = (
    ('aaron', 8312), ('angela', 7603), ('dave', 7306),
    ('davina',7902), ('elliot', 7911), ('ernie', 7410),
    ('jess', 7912), ('jim', 7512), ('larry', 7311),
    ('leslie', 7808), ('melissa', 8602), ('pat', 7711),
    ('serena', 7003), ('stan', 7607), ('faye', 6812),
    ('amy', 7209),
)

#下面是一个创建函数,当调用此函数时,得到一个生成器
def randName():
    pick = list(NAMES)
    while len(pick) > 0:
        yield pick.pop(rrange(len(pick)))

#下面的函数进行数据库中表users的写入操作
def insert(cur, db):
    if db == 'sqlite':
        cur.executemany("INSERT INTO users VALUES(?, ?, ?)",
        [(who, uid, rrange(1,5)) for who, uid in randName()])
    elif db == 'gadfly':
        for who, uid in randName():
            cur.execute("INSERT INTO users VALUES(?, ?, ?)",
            (who, uid, rrange(1,5)))

    #下面将NAMES写进数据库
    elif db == 'mysql':
        cur.executemany("INSERT INTO users VALUES(%s, %s, %s)",
        [(who, uid, rrange(1,5)) for who, uid in randName()])

#cur.rowcount用来记录最后一次execute影响的行数
getRC = lambda cur: cur.rowcount if hasattr(cur, 'rowcount') else -1

#下面执行数据更新
def update(cur):
    fr = rrange(1,5)
    to = rrange(1,5)
    cur.execute(
        "UPDATE users SET prid=%d WHERE prid=%d" % (to, fr))
    return fr, to, getRC(cur)

#随机删除一些行
def delete(cur):
    rm = rrange(1,5)
    cur.execute('DELETE FROM users WHERE prid=%d' % rm)
    return rm, getRC(cur)

def dbDump(cur):
    cur.execute('SELECT * FROM users')
    print '
%s%s%s' % ('LOGIN'.ljust(COLSIZ),
        'USERID'.ljust(COLSIZ), 'PROJ#'.ljust(COLSIZ))
    for data in cur.fetchall():
        print '%s%s%s' % tuple([str(s).title().ljust(COLSIZ) for s in data])

def main():
    db = setup()
    print '*** Connecting to %r database' % db
    cxn = connect(db, 'test')
    if not cxn:
        print '
ERROR: %r not supported, exiting' % db
        return
    #游标对象
    cur = cxn.cursor()

    print '
*** Creating users table'
    create(cur)

    print '
*** Inserting names into table'
    insert(cur, db)
    dbDump(cur)

    print '
*** Randomly moving folks',
    fr, to, num = update(cur)
    print 'from one group (%d) to another (%d)' % (fr, to)
    print '	(%d users moved)' % num
    dbDump(cur)

    print '
*** Randomly choosing group',
    rm, num = delete(cur)
    print '(%d) to delete' % rm
    print '	(%d users removed)' % num
    dbDump(cur)

    print '
*** Dropping users table'
    drop(cur)
    cur.close()
    cxn.commit()
    cxn.close()

if __name__ == '__main__':
    main()

上面的例子只是应用了MySQL数据库,其他两个没试验。

21.3 对象-关系管理器(ORM)

ORM即对象-映射关系(OBJECT/RELATION MAPPING),是一种程序技术,用于实现面向对象编程语言里不同类型系统的数据之间的转换。也就是将Python对象与SQL进行映射转换。

如果用ORM,那么需要尽量避免直接的SQL查询。数据库中的表被转换为Python类,它具有列属性和操作数据库的方法。

最知名的Python ORM模块是SQLAlchemy 和 SQLObject。书上说两者有着不同的设计哲学...

1、SQLAlchemy的接口在某种程度上更接近SQL。SQLAlchemy的抽象层‘相当完美’,并且在必须用SQL完成某些功能时,它提供了足够的灵活性。‘其实这两个ORM模块在设置及存取数据时使用的术语非常相似,代码长度也相似。’

这部分课本上的代码过时了。。

下面教程很好,谢谢了,http://blog.csdn.net/billvsme/article/details/46783491

官方文档:http://docs.sqlalchemy.org/en/rel_1_0/

原文地址:https://www.cnblogs.com/batteryhp/p/5218895.html