ZetCode

Python sqlite3.register_converter 函数

上次修改时间:2025 年 4 月 15 日

本综合指南探讨了 Python 的 sqlite3.register_converter 函数,该函数允许在从 SQLite 检索数据时进行自定义类型转换。

基本定义

sqlite3.register_converter 函数注册一个可调用对象,用于将特定类型的 SQLite 值转换为 Python 对象。它与 sqlite3.connect 中的 detect_types 参数一起使用。

主要特征:它将 SQLite 类型映射到 Python 对象,支持复杂的类型处理,并与内置类型和自定义类型一起使用。转换发生在从数据库获取数据时。

基本类型转换

此示例显示如何为 SQLite DATE 类型注册一个转换器,将其转换为 Python date 对象。

basic_converter.py
import sqlite3
from datetime import date

def convert_date(val):
    return date.fromisoformat(val.decode())

with sqlite3.connect(':memory:', detect_types=sqlite3.PARSE_DECLTYPES) as conn:
    sqlite3.register_converter("DATE", convert_date)
    
    conn.execute("CREATE TABLE events(id INTEGER, event_date DATE)")
    conn.execute("INSERT INTO events VALUES (1, '2025-04-15')")
    
    row = conn.execute("SELECT * FROM events").fetchone()
    print(row[1], type(row[1]))  # 2025-04-15 <class 'datetime.date'>

转换器函数接受来自 SQLite 的字节对象并返回一个 Python date。 PARSE_DECLTYPES 标志启用从列声明中进行类型检测。

此模式对于在提取操作期间自动将数据库原生格式转换为 Python 对象非常有用。

转换 JSON 数据

此示例演示了如何自动将存储在 SQLite 中的 JSON 字符串转换为 Python 字典。

json_converter.py
import sqlite3
import json

def convert_json(val):
    return json.loads(val.decode())

with sqlite3.connect(':memory:', detect_types=sqlite3.PARSE_DECLTYPES) as conn:
    sqlite3.register_converter("JSON", convert_json)
    
    conn.execute("CREATE TABLE configs(id INTEGER, settings JSON)")
    conn.execute("INSERT INTO configs VALUES (1, '{\"theme\": \"dark\", \"notifications\": true}')")
    
    row = conn.execute("SELECT * FROM configs").fetchone()
    print(row[1]['theme'])  # dark

转换器在检索数据时自动反序列化 JSON 字符串。 这对于在 SQLite 中存储半结构化数据特别有用。

请注意,SQLite 没有原生的 JSON 类型 - 我们使用类型名称作为转换器识别的约定。

自定义对象转换

此示例显示如何将 SQLite 值转换为自定义 Python 对象。

custom_object.py
import sqlite3

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y
    
    def __repr__(self):
        return f"Point({self.x}, {self.y})"

def convert_point(val):
    x, y = map(float, val.decode().split(','))
    return Point(x, y)

with sqlite3.connect(':memory:', detect_types=sqlite3.PARSE_DECLTYPES) as conn:
    sqlite3.register_converter("POINT", convert_point)
    
    conn.execute("CREATE TABLE shapes(id INTEGER, location POINT)")
    conn.execute("INSERT INTO shapes VALUES (1, '3.5,4.2')")
    
    row = conn.execute("SELECT * FROM shapes").fetchone()
    print(row[1])  # Point(3.5, 4.2)

在这里,我们将点作为逗号分隔的字符串存储在 SQLite 中,并在检索时将其转换为 Point 对象。 转换器处理解析逻辑。

此模式对于需要在关系数据库中持久化的特定于域的对象很有用。

二进制数据转换

此示例演示了将二进制数据转换为自定义 Python 对象。

binary_converter.py
import sqlite3
import pickle

class CustomData:
    def __init__(self, data):
        self.data = data
    
    def __repr__(self):
        return f"CustomData({self.data!r})"

def convert_custom(val):
    return pickle.loads(val)

with sqlite3.connect(':memory:', detect_types=sqlite3.PARSE_DECLTYPES) as conn:
    sqlite3.register_converter("CUSTOM", convert_custom)
    
    original = CustomData([1, 2, 3])
    conn.execute("CREATE TABLE data_store(id INTEGER, data CUSTOM)")
    conn.execute("INSERT INTO data_store VALUES (1, ?)", 
                (pickle.dumps(original),))
    
    row = conn.execute("SELECT * FROM data_store").fetchone()
    print(row[1])  # CustomData([1, 2, 3])

此示例使用 pickle 将 Python 对象序列化为二进制格式,以便存储在 SQLite 中。 转换器在检索时取消 pickle 数据。

虽然功能强大,但在加载不受信任的数据时,请谨慎使用 pickle,因为它存在安全隐患。

多个转换器

此示例显示了为不同的 SQLite 类型注册多个转换器。

multiple_converters.py
import sqlite3
from datetime import datetime, date

def convert_datetime(val):
    return datetime.strptime(val.decode(), "%Y-%m-%d %H:%M:%S")

def convert_date(val):
    return date.fromisoformat(val.decode())

with sqlite3.connect(':memory:', detect_types=sqlite3.PARSE_DECLTYPES) as conn:
    sqlite3.register_converter("DATETIME", convert_datetime)
    sqlite3.register_converter("DATE", convert_date)
    
    conn.execute("""CREATE TABLE events(
        id INTEGER, 
        event_date DATE,
        created_at DATETIME)""")
    
    conn.execute("""INSERT INTO events VALUES 
        (1, '2025-04-15', '2025-04-15 14:30:00')""")
    
    row = conn.execute("SELECT * FROM events").fetchone()
    print(f"Date: {row[1]}, Datetime: {row[2]}")

在这里,我们为 DATE 和 DATETIME 类型注册单独的转换器。 每个转换器都处理其特定格式并返回适当的 Python 对象。

这种方法可以对如何将不同的 SQLite 类型转换为 Python 对象进行细粒度的控制。

转换器中的错误处理

此示例演示了转换器函数中的正确错误处理。

error_handling.py
import sqlite3
from datetime import date

def safe_date_converter(val):
    try:
        return date.fromisoformat(val.decode())
    except (ValueError, AttributeError) as e:
        print(f"Conversion error: {e}")
        return None

with sqlite3.connect(':memory:', detect_types=sqlite3.PARSE_DECLTYPES) as conn:
    sqlite3.register_converter("DATE", safe_date_converter)
    
    conn.execute("CREATE TABLE events(id INTEGER, event_date DATE)")
    conn.execute("INSERT INTO events VALUES (1, 'invalid-date')")
    
    row = conn.execute("SELECT * FROM events").fetchone()
    print(row[1])  # None (due to conversion error)

转换器包括错误处理以管理格式错误的数据。 当转换失败时,它返回 None,从而防止应用程序崩溃。

强大的转换器应始终处理输入数据中的潜在错误,以保持应用程序的稳定性。

与适配器结合

此示例显示了将转换器与适配器一起使用以进行双向转换。

adapter_converter.py
import sqlite3
from decimal import Decimal

def adapt_decimal(d):
    return str(d)

def convert_decimal(val):
    return Decimal(val.decode())

# Register the adapter and converter
sqlite3.register_adapter(Decimal, adapt_decimal)
sqlite3.register_converter("DECIMAL", convert_decimal)

with sqlite3.connect(':memory:', detect_types=sqlite3.PARSE_DECLTYPES) as conn:
    conn.execute("CREATE TABLE prices(id INTEGER, amount DECIMAL)")
    
    price = Decimal('19.99')
    conn.execute("INSERT INTO prices VALUES (1, ?)", (price,))
    
    row = conn.execute("SELECT * FROM prices").fetchone()
    print(row[1], type(row[1]))  # 19.99 <class 'decimal.Decimal'>

适配器在存储数据时将 Python 对象转换为与 SQLite 兼容的格式,而转换器在检索时将其转换回来。 这提供了无缝的双向转换。

该组合功能强大,可以处理自定义类型,同时保持数据库兼容性。

最佳实践

资料来源

作者

我叫 Jan Bodnar,是一位充满热情的程序员,拥有丰富的编程经验。 自 2007 年以来,我一直在撰写编程文章。 迄今为止,我已经撰写了超过 1,400 篇文章和 8 本电子书。 我拥有超过十年的编程教学经验。

列出所有 Python 教程