1 pickle存储+文本存储——容易出现数据不一致
当有数据需要更新时,前面章节所采取的“pickle+文本”的存储模式会存在问题,如下图所示:
如果更新pickle,下一次put_to_store()函数运行时,罪行的更新会丢失,因为put_to_store()会由文本文件中的数据重新创建pickle。
如果更新文本文件,则直到put_to_store()再次运行时,pickle才会得到更新,造成用户调用不一致的情况。中不一致成为竞态条件。要尽量避免这种情况发生,就需要采取更好的存储机制。
如果能把数据只存储在一个地方,并能支持相应的需求,那该多好!
这就是——数据库。
我们有许多选择:
这些都很好,但是对于我们的应用 优点大材小用。
Python自带的sqlite
2 用sqlite存储所有的数据
2.1 python与sqlite
python预装了sqlite,这是一个相当完备、无需配置的基于SQL的数据管理系统。
要使用sqlite只需导入sqlite库,并使用python标准化数据库API来编程,不需要数据库设置,没有配置,也没有后续维护。
2.2 python的数据库接口(API)
python数据库API提供了一种标准机制,可以针对各种各样的数据库管理系统编程,其中包括sqlite,不论后台数据库是什么,代码遵循的过程基本一致:
下面图是python与sqlite交互的代码示例:
2.3 pickle存储模式
数据在pickle中存储的方法如下图所示,每个选手的数据是一个AthleteList对象实例与字典中的选手名相关联,整个字典存放在pickle中。
每个AthleteList有以下属性:
从这种设计模式可以清楚的看出哪个名字、出生日期于那个选手相对,如何在sqlite数据库中建立起这种关系呢?
2.4 sqlite存储模式
创建数据库coachdata.sqlite
,它包含两个表:athletes和timing_data。
2.5 创建sqlite数据库
创建的方法是用python API编写相关的代码,将程序命名为createDBtables.py
其代码如下:
import sqlite3
connection = sqlite3.connect('coachdata.sqlite')
cursor = connection.cursor()
cursor.executescript("""
CREATE TABLE athletes(
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL UNIQUE,
name TEXT NOT NULL,
dob DATE NOT NULL);
""")
cursor.executescript("""
CREATE TABLE timing_data(
athlete_id INTEGER NOT NULL,
value TEXT NOT NULL,
FOREIGN KEY (athlete_id) REFERENCES athletes)
""")
connection.commit()
connection.close()
其中,创建表athletes
的第一行代码,按照《Head First Python》上的:
id INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE NOT NULL
会出错,而写成:
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL UNIQUE
才可以。
2.6 从pickle向sqlite导入数据
创建好数据库和表,需要将数据从现在的模型(pickle+文本)传输到数据库模型中。可以编写代码来完成这个工作,先给出initDBathletes.py的完整代码:
import sqlite3
connection = sqlite3.connect('coachdata.sqlite')
cursor = connection.cursor()
import glob
import athletemodel
data_files = glob.glob("../data/*.txt")
athletes = athletemodel.put_to_store(data_files)
for each_ath in athletes:
name = athletes[each_ath].name
dob = athletes[each_ath].dob
cursor.execute("INSERT INTO athletes (name,dob) VALUES (?,?)",(name,dob))
connection.commit()
cursor.execute("SELECT id from athletes WHERE name=? AND dob=?",(name,dob))
the_current_id=cursor.fetchone()[0]
for each_time in athletes[each_ath].clean_data:
cursor.execute("INSERT INTO timing_data (athlete_id,value) VALUES (?,?)",
(the_current_id,each_time))
connection.commit()
connection.close()
第1步:将运动员的文本数据转换为AthleteList实例:
data_files = glob.glob("../data/*.txt")
athletes = athletemodel.put_to_store(data_files)
第2步:逐一将每个运动员的数据加入到表中:
(1)首先是加入运动员的姓名和出生日期
name = athletes[each_ath].name
dob = athletes[each_ath].dob
cursor.execute("INSERT INTO athletes (name,dob) VALUES (?,?)",(name,dob))
connection.commit()
(2)查询刚才加入的运动员的id,将运动员的id和运动员的成绩逐一增加到成绩表timing_data
中。
cursor.execute("SELECT id from athletes WHERE name=? AND dob=?",(name,dob))
the_current_id=cursor.fetchone()[0]
for each_time in athletes[each_ath].clean_data:
cursor.execute("INSERT INTO timing_data (athlete_id,value) VALUES (?,?)",
(the_current_id,each_time))
connection.commit()
2.7 sqlite数据查看——SQLite Manager
安装工具软件SQLite Manager便可方便查看sqlite数据库的数据。