有什么我应该注意的问题吗?我可以将它存储在文本字段中,还是需要使用 blob?(我对 pickle 或 sqlite 都不太熟悉,所以我想确保我用我的一些高级设计理念在正确的树上吠叫。)
14 回答
我也需要实现同样的目标。
事实证明,在我终于弄清楚之前,这让我很头疼,多亏了这篇文章,如何使它以二进制格式实际工作。
插入/更新:
pdata = cPickle.dumps(data, cPickle.HIGHEST_PROTOCOL)
curr.execute("insert into table (data) values (:data)", sqlite3.Binary(pdata))
您必须指定转储的第二个参数以强制执行二进制酸洗。
另请注意sqlite3.Binary以使其适合 BLOB 字段。
要检索数据:
curr.execute("select data from table limit 1")
for row in curr:
data = cPickle.loads(str(row['data']))
在检索 BLOB 字段时,sqlite3 获取一个“缓冲区”python 类型,需要在传递给加载方法之前使用str进行字符串化。
如果要存储腌制对象,则需要使用 blob,因为它是二进制数据。但是,例如,您可以对腌制对象进行 base64 编码以获取可以存储在文本字段中的字符串。
不过,一般来说,做这种事情表明设计不好,因为您存储的是不透明的数据,您将失去使用 SQL 对该数据进行任何有用操作的能力。尽管不知道您实际上在做什么,但我无法真正对此做出道德呼吁。
我写了一篇关于这个想法的博客,除了我使用 json 而不是 pickle,因为我希望它可以与 perl 和其他程序互操作。
http://writeonly.wordpress.com/2008/12/05/simple-object-db-using-json-and-python-sqlite/
从架构上讲,这是一种为任意数据结构获取持久性、事务等的快速而肮脏的方法。我发现这种组合在我想要持久性时非常有用,并且不需要在 sql 层对数据做太多事情(或者在 sql 中处理非常复杂,而使用生成器很简单)。
代码本身非常简单:
# register the "loader" to get the data back out.
sqlite3.register_converter("pickle", cPickle.loads)
然后,当你想把它转储到数据库中时,
p_string = p.dumps( dict(a=1,b=[1,2,3]))
conn.execute('''
create table snapshot(
id INTEGER PRIMARY KEY AUTOINCREMENT,
mydata pickle);
''')
conn.execute('''
insert into snapshot values
(null, ?)''', (p_string,))
''')
Pickle 有文本和二进制输出格式。如果您使用基于文本的格式,则可以将其存储在 TEXT 字段中,但如果您使用(更有效的)二进制格式,则它必须是 BLOB。
我不得不同意这里的一些评论。小心并确保您真的想将泡菜数据保存在数据库中,可能有更好的方法。
无论如何,我过去在尝试将二进制数据保存在 sqlite 数据库中时遇到了麻烦。显然你必须使用 sqlite3.Binary() 来为 sqlite 准备数据。
这是一些示例代码:
query = u'''insert into testtable VALUES(?)'''
b = sqlite3.Binary(binarydata)
cur.execute(query,(b,))
con.commit()
由于 Pickle 可以将您的对象图转储为字符串,因此应该可以。
请注意,尽管 SQLite 中的 TEXT 字段使用数据库编码,因此您可能需要在取消腌制之前将其转换为简单字符串。
如果字典可以腌制,它也可以存储在 text/blob 字段中。
请注意不能腌制的字典(也就是包含不可腌制的对象)。
是的,正如其他人所解释的那样,您可以将腌制对象存储在 SQLite3 数据库的 TEXT 或 BLOB 字段中。
请注意,某些对象不能腌制。内置容器类型可以(dict、set、list、tuple 等)。但是有些对象,比如文件句柄,引用了它们自己的数据结构之外的状态,而其他扩展类型也有类似的问题。
由于字典可以包含任意嵌套的数据结构,因此它可能不是可腌制的。
SpoonMeiser 是正确的,您需要有充分的理由来腌制数据库。
使用 SQLite 编写实现持久性的 Python 对象并不难。然后,您也可以使用 SQLite CLI 来处理数据。根据我的经验,这值得额外的工作,因为许多调试和管理功能可以简单地从 CLI 执行,而不是编写特定的 Python 代码。
在项目的早期阶段,我按照您的建议做了,最后为每个业务对象重新编写了一个 Python 类(注意:我没有说每个表!)这样应用程序的主体可以专注于需要“做什么”而不是“如何”完成。
考虑到您的要求是保存一个字典,然后为了用户的“观看乐趣”而将其吐出,另一个选择是使用该shelve
模块,它可以让您将任何可腌制的数据保存到文件中。python 文档在这里。
可以将对象数据存储为 pickle dump、jason 等,但也可以对它们进行索引、限制它们并运行使用这些索引的选择查询。这是元组的示例,可以轻松应用于任何其他 python 类。所有需要的都在 python sqlite3 文档中进行了解释(有人已经发布了链接)。无论如何,这里的所有内容都放在以下示例中:
import sqlite3
import pickle
def adapt_tuple(tuple):
return pickle.dumps(tuple)
sqlite3.register_adapter(tuple, adapt_tuple) #cannot use pickle.dumps directly because of inadequate argument signature
sqlite3.register_converter("tuple", pickle.loads)
def collate_tuple(string1, string2):
return cmp(pickle.loads(string1), pickle.loads(string2))
#########################
# 1) Using declared types
con = sqlite3.connect(":memory:", detect_types=sqlite3.PARSE_DECLTYPES)
con.create_collation("cmptuple", collate_tuple)
cur = con.cursor()
cur.execute("create table test(p tuple unique collate cmptuple) ")
cur.execute("create index tuple_collated_index on test(p collate cmptuple)")
cur.execute("select name, type from sqlite_master") # where type = 'table'")
print(cur.fetchall())
p = (1,2,3)
p1 = (1,2)
cur.execute("insert into test(p) values (?)", (p,))
cur.execute("insert into test(p) values (?)", (p1,))
cur.execute("insert into test(p) values (?)", ((10, 1),))
cur.execute("insert into test(p) values (?)", (tuple((9, 33)) ,))
cur.execute("insert into test(p) values (?)", (((9, 5), 33) ,))
try:
cur.execute("insert into test(p) values (?)", (tuple((9, 33)) ,))
except Exception as e:
print e
cur.execute("select p from test order by p")
print "\nwith declared types and default collate on column:"
for raw in cur:
print raw
cur.execute("select p from test order by p collate cmptuple")
print "\nwith declared types collate:"
for raw in cur:
print raw
con.create_function('pycmp', 2, cmp)
print "\nselect grater than using cmp function:"
cur.execute("select p from test where pycmp(p,?) >= 0", ((10, ),) )
for raw in cur:
print raw
cur.execute("explain query plan select p from test where p > ?", ((3,)))
for raw in cur:
print raw
print "\nselect grater than using collate:"
cur.execute("select p from test where p > ?", ((10,),) )
for raw in cur:
print raw
cur.execute("explain query plan select p from test where p > ?", ((3,)))
for raw in cur:
print raw
cur.close()
con.close()
许多应用程序使用 sqlite3 作为 SQLAlchemy 的后端,因此自然也可以在 SQLAlchemy 框架中提出这个问题(这就是我遇到这个问题的方式)。
为此,需要定义希望存储泡菜数据的列以存储“PickleType”数据。实现非常简单:
from sqlalchemy import PickleType, Integer
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine
import pickle
Base= declarative_base()
class User(Base):
__tablename__= 'Users'
id= Column(Integer, primary_key= True)
user_login_data_array= Column(PickleType)
login_information= {'User1':{'Times': np.arange(0,20),
'IP': ['123.901.12.189','123.441.49.391']}}
engine= create_engine('sqlite:///memory:',echo= False)
Base.metadata.create_all(engine)
Session_maker= sessionmaker(bind=engine)
Session= Session_maker()
# The pickling here is very intuitive! Just need to have
# defined the column "user_login_data_array" to take pickletype data.
pickled_login_data_array= pickle.dumps(login_information)
user_object_to_add= User(user_login_data_array= pickled_login_data_array)
Session.add(user_object_to_add)
Session.commit()
(我并不是说这个例子最适合使用泡菜,因为其他人已经注意到了问题。)
在 SourceForge 上查看此解决方案:
y_serial.py 模块 :: 使用 SQLite 存储 Python 对象
“序列化 + 持久性 :: 在几行代码中,将 Python 对象压缩和注释为 SQLite;然后在没有任何 SQL 的情况下通过关键字按时间顺序检索它们。数据库存储无模式数据的最有用的“标准”模块。”