【问题标题】:Can I pickle a python dictionary into a sqlite3 text field?我可以将 python 字典腌制到 sqlite3 文本字段中吗?
【发布时间】:2010-09-16 22:55:37
【问题描述】:

我应该注意什么问题?我可以将它存储在文本字段中,还是需要使用 blob? (我对 pickle 或 sqlite 都不太熟悉,所以我想确保我的一些高级设计想法是正确的。)

【问题讨论】:

  • 这完全取决于您的字典包含的内容。而且,这听起来很像你在做坏事。您使用数据库的原因是什么?您选择 SQLite 的原因是什么?
  • 好消息是,这是一个小型应用程序,最多可供大约 5 人使用,只需要保存一些数据并将其吐出即可。 pickle -> sqlite 的想法是在几次太多的山露之后产生的,但我很好奇它是否可能。

标签: python sqlite pickle


【解决方案1】:

如果要存储腌制对象,则需要使用 blob,因为它是二进制数据。但是,你可以说,base64 对腌制对象进行编码以获得可以存储在文本字段中的字符串。

不过,一般来说,做这种事情表明设计不好,因为您存储的是不透明的数据,您将失去使用 SQL 对该数据进行任何有用操作的能力。虽然不知道你实际上在做什么,但我无法真正对它做出道德呼吁。

【讨论】:

  • 是的,我知道这是一个有点可疑的设计。有问题的数据永远不需要被选择,它只需要被保存,然后为了用户的“观看乐趣”而吐出来。 (无论如何,这是规范作者告诉我的。)
  • 只要你这样做的前提是这是一个快速而肮脏的解决方案,而不是认为这是一个非常巧妙和聪明的技巧,我认为你会没事的。
【解决方案2】:

由于 Pickle 可以将您的对象图转储为字符串,因此应该可以。

请注意,SQLite 中的 TEXT 字段使用数据库编码,因此您可能需要在取消腌制之前将其转换为简单字符串。

【讨论】:

    【解决方案3】:

    Pickle 具有文本和二进制输出格式。如果您使用基于文本的格式,则可以将其存储在 TEXT 字段中,但如果您使用(更有效的)二进制格式,则它必须是 BLOB。

    【讨论】:

      【解决方案4】:

      如果字典可以腌制,它也可以存储在 text/blob 字段中。

      请注意不能腌制的字典(也就是包含不可腌制的对象)。

      【讨论】:

        【解决方案5】:

        是的,您可以将腌制对象存储在 SQLite3 数据库中的 TEXT 或 BLOB 字段中,正如其他人所解释的那样。

        请注意,某些对象不能腌制。内置容器类型可以(dict、set、list、tuple 等)。但是有些对象,比如文件句柄,引用了自身数据结构之外的状态,其他扩展类型也有类似的问题。

        由于字典可以包含任意嵌套的数据结构,因此它可能不是可腌制的。

        【讨论】:

          【解决方案6】:

          SpoonMeiser 是正确的,您需要有充分的理由来腌制数据库。

          使用 SQLite 编写实现持久性的 Python 对象并不难。然后,您也可以使用 SQLite CLI 来处理数据。以我的经验,这值得额外的工作,因为许多调试和管理功能可以简单地从 CLI 执行,而不是编写特定的 Python 代码。

          在项目的早期阶段,我按照您的建议做了,最后为每个业务对象重新编写了一个 Python 类(注意:我没有说每个表!)这样应用程序的主体可以专注于需要完成的“什么”而不是“如何”完成。

          【讨论】:

            【解决方案7】:

            考虑到您的要求是保存一个字典,然后为了用户的“观看乐趣”而将其吐出,另一个选择是使用shelve 模块,它可以让您将任何可腌制的数据保存到文件中。 python 文档是here

            【讨论】:

              【解决方案8】:

              根据您的工作,您可能需要查看shove 模块。它做了类似的事情,它在 sqlite 数据库中自动存储 Python 对象(以及各种其他选项)并伪装成字典(就像 shelve 模块一样)。

              【讨论】:

                【解决方案9】:

                我必须同意这里的一些 cmets。小心并确保您真的想将泡菜数据保存在数据库中,可能有更好的方法。

                无论如何,我过去在尝试将二进制数据保存在 sqlite 数据库中时遇到了麻烦。 显然你必须使用 sqlite3.Binary() 来为 sqlite 准备数据。

                这里有一些示例代码:

                query = u'''insert into testtable VALUES(?)'''
                b = sqlite3.Binary(binarydata)
                cur.execute(query,(b,))
                con.commit()
                

                【讨论】:

                  【解决方案10】:

                  我写了一篇关于这个想法的博客,除了 pickle 之外,我使用了 json,因为我希望它可以与 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,))
                  ''')
                  

                  【讨论】:

                    【解决方案11】:

                    在 SourceForge 上查看此解决方案:

                    y_serial.py 模块 :: 使用 SQLite 存储 Python 对象

                    “序列化 + 持久化:: 在几行代码中,将 Python 对象压缩和注释为 SQLite;然后在不使用任何 SQL 的情况下通过关键字按时间顺序检索它们。数据库存储无模式数据的最有用的“标准”模块。”

                    http://yserial.sourceforge.net

                    【讨论】:

                      【解决方案12】:

                      我也需要实现同样的目标。

                      在我终于弄清楚thanks to this post,如何让它以二进制格式工作之前,我发现它让我很头疼。

                      插入/更新:

                      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 对其进行字符串化。

                      【讨论】:

                      • 我爱你,伙计!这解决了我在使用 Redis 时遇到的问题!我没有指定协议,虽然我没有收到任何 unpickling 错误,但我收到的数据不一致可能是因为一些编码问题。这有意义吗?
                      【解决方案13】:

                      可以将对象数据存储为 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()
                      

                      【讨论】:

                        【解决方案14】:

                        许多应用程序使用 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()
                        

                        (我并不是说这个例子最适合使用 pickle,因为其他人已经注意到了问题。)

                        【讨论】:

                          猜你喜欢
                          • 2018-03-12
                          • 1970-01-01
                          • 2019-10-31
                          • 1970-01-01
                          • 2011-05-04
                          • 2017-08-14
                          • 2018-08-18
                          • 2019-04-13
                          • 2016-01-05
                          相关资源
                          最近更新 更多