11.3 从Python对象到SQLite BLOB列的映射

    我们可以将 SQL 列映射为类的定义,这样一来就能够基于数据库中的数据来构造适当的Python对象。SQLite中包含了一个二进制大对象(Binary Large Object,BLOB)数值类型。我们可以使用pickle来处理Python对象,然后将它们存入BLOB列中。可以使用字符串来表示Python对象(例如,使用JSON或YAML格式),也可以使用SQLite中的文本列。

    在使用这种技术时应当谨慎,因为它很容易会妨碍SQL的处理过程。一个BLOB列无法用于SQL中DML的处理。我们无法为它建立索引或用在DML语句中的查找关键字中。

    当不需要考虑SQL处理过程的透明度时,SQLite的BLOB映射过程应在对象中完成。最常见的例子是媒体对象,例如视频和图片或语音片段。SQL偏向于文本和数字字段。它通常不会处理更复杂的对象。

    当需要处理金融数据时,在应用程序中需要使用decimal.Decimal数值。可能会希望在SQL中查询或计算这类数据。由于decimal.Decimal并没有被SQLite直接支持,需要对SQLite进行扩展来处理这种类型的数据。

    存在这样两种方式:转换和适配。我们需要对 Python 的数据进行适配,存入 SQLite需要将SQLite中的数据转换到Python中。以下为这两种函数以及用于完成注册的代码。

    import decimal
    def adapt_currency(value):
      return str(value)
    sqlite3.register_adapter(decimal.Decimal, adapt_currency)

    def convert_currency(bytes):
      return decimal.Decimal(bytes.decode())
    sqlite3.register_converter("DECIMAL", convert_currency)

    我们写了一个adapt_currency()函数,用于完成将decimal.Decimal对象适配为数据库中适当的形式。在以上例子中,我们只是做了一个到字符串的转换。由于已经注册了适配函数,因此SQLite接口就能够使用所注册的适配函数来完成decimal.Decimal类对象的转换逻辑了。

    我们也写了一个convert_currency()函数,用于从SQLite字节对象转换为Python中的decimal.Decimal对象。由于注册了转换函数,因此DECIMAL类型的列就能被适当地转换为Python中的对象。

    一旦定义了适配器和转化器,我们就能将 DECIMAL 看作一种被完全支持的列类型。除此之外,还要在建立数据库连接时通过设置 detect_types=sqlite3. PARSE_DECLTYPES 来通知 SQLite。如下是一个表定义的例子,其中包含了使用新数值类型的列。

    CREATE TABLE BUDGET(
      year INTEGER,
      month INTEGER,
      category TEXT,
      amount DECIMAL
    )

    我们可以像这样来使用新列。

    database= sqlite3.connect( 'p2_c11_blog.db', detect_types=sqlite3.
    PARSE_DECLTYPES )
    database.execute( decimal_ddl )

    insert_budget= """
    INSERT INTO BUDGET(year, month, category, amount) VALUES(:year,
    :month, :category, :amount)
    """
    database.execute( insert_budget,
      dict(year=2013, month=1, category="fuel", amount=decimal.
    Decimal('256.78')) )
    database.execute( insert_budget,
      dict(year=2013, month=2, category="fuel", amount=decimal.
    Decimal('287.65')) )

    query_budget= """
    SELECT * FROM BUDGET
    """
    for row in database.execute( query_budget ):
      print( row )

    我们创建了一个数据库连接,需要定义的类型通过转换函数来完成映射。一旦创建了连接,就能在创建表时使用一个DECIMAL类型的列。

    当向表中插入行时,我们使用了decimal.Decimal对象。当从表中获取行时,可以看到我们从数据库中拿到了decimal.Decimal对象。如下是输出结果。

    (2013, 1, 'fuel', Decimal('256.78'))
    (2013, 2, 'fuel', Decimal('287.65'))

    以上代码表明decimal.Decimal对象被正确地从数据库中储存并获取。我们可以为任何Python类编写适配器和转化器,可能也需要创建一种二进制的表示。由于从字符串转换到二进制很容易,因此直接使用字符串通常是最简单的方式。