ZetCode

Python sqlite3.PARSE_DECLTYPES 常量

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

这份全面的指南探讨了 Python 的 sqlite3.PARSE_DECLTYPES 常量,它可以在从 SQLite 数据库检索数据时实现自动类型转换。我们将涵盖它的用法、优点和实际示例。

基本定义

sqlite3.PARSE_DECLTYPES 常量与 sqlite3.connect 一起使用,以启用基于列声明的类型转换。设置后,SQLite 将尝试将值转换为与列的声明类型匹配的 Python 类型。

主要特征:它与 detect_types 参数一起使用,支持标准的 Python 类型,如 datetime.date,并且需要在表定义中进行正确的列类型声明。

PARSE_DECLTYPES 的基本用法

这是一个简单的示例,演示如何使用 PARSE_DECLTYPES 自动转换日期值类型。

basic_parse_decltypes.py
import sqlite3
import datetime

# Connect with PARSE_DECLTYPES enabled
with sqlite3.connect(':memory:', detect_types=sqlite3.PARSE_DECLTYPES) as conn:
    cursor = conn.cursor()
    
    # Create table with DATE type declaration
    cursor.execute('''CREATE TABLE events
                      (id INTEGER PRIMARY KEY, name TEXT, event_date DATE)''')
    
    # Insert current date
    today = datetime.date.today()
    cursor.execute("INSERT INTO events (name, event_date) VALUES (?, ?)",
                  ('Meeting', today))
    
    # Retrieve and verify type
    cursor.execute("SELECT event_date FROM events")
    retrieved_date = cursor.fetchone()[0]
    print(f"Type of retrieved date: {type(retrieved_date)}")  # datetime.date
    print(f"Date matches original: {retrieved_date == today}")  # True

此示例展示了 PARSE_DECLTYPES 如何自动将 SQLite DATE 值转换为 Python datetime.date 对象。转换发生在数据检索期间。

请注意正确声明列类型(在本例中为 DATE)的重要性,以便转换正常工作。如果没有类型声明,则不会发生转换。

使用 DATETIME 值

当在表架构中正确声明时,PARSE_DECLTYPES 也可以处理 datetime 值。

datetime_example.py
import sqlite3
from datetime import datetime

with sqlite3.connect(':memory:', detect_types=sqlite3.PARSE_DECLTYPES) as conn:
    cursor = conn.cursor()
    
    # Create table with TIMESTAMP type declaration
    cursor.execute('''CREATE TABLE logs
                      (id INTEGER PRIMARY KEY, message TEXT, 
                       created_at TIMESTAMP)''')
    
    # Insert current datetime
    now = datetime.now()
    cursor.execute("INSERT INTO logs (message, created_at) VALUES (?, ?)",
                  ('System started', now))
    
    # Retrieve and verify datetime
    cursor.execute("SELECT created_at FROM logs")
    retrieved_dt = cursor.fetchone()[0]
    print(f"Type: {type(retrieved_dt)}")  # datetime.datetime
    print(f"Value: {retrieved_dt}")
    print(f"Microseconds preserved: {retrieved_dt.microsecond == now.microsecond}")

此示例演示了将 TIMESTAMP 列自动转换为 Python datetime.datetime 对象。转换保留所有 datetime 组件,包括微秒。

关键要求是在表架构中将列声明为 TIMESTAMP。 诸如 DATETIME 之类的其他声明也适用于此转换。

与 PARSE_COLNAMES 结合使用

PARSE_DECLTYPES 可以与 PARSE_COLNAMES 结合使用,以实现更灵活的类型转换场景。

combined_parsing.py
import sqlite3
from decimal import Decimal

# Register converter for DECIMAL type
sqlite3.register_converter("DECIMAL", lambda x: Decimal(x.decode('utf-8')))

with sqlite3.connect(':memory:', 
                    detect_types=sqlite3.PARSE_DECLTYPES | sqlite3.PARSE_COLNAMES) as conn:
    cursor = conn.cursor()
    
    # Create table with mixed types
    cursor.execute('''CREATE TABLE products
                      (id INTEGER PRIMARY KEY, name TEXT, 
                       price DECIMAL, weight REAL)''')
    
    # Insert data with decimal value
    cursor.execute("INSERT INTO products (name, price, weight) VALUES (?, ?, ?)",
                  ('Laptop', '1299.99', 2.5))
    
    # Query with type hints in column names
    cursor.execute('''SELECT price AS "price [DECIMAL]", 
                             weight AS "weight [REAL]" 
                      FROM products''')
    
    product = cursor.fetchone()
    print(f"Price type: {type(product[0])}")  # decimal.Decimal
    print(f"Weight type: {type(product[1])}")  # float

此示例展示了如何结合使用两种解析模式。 PARSE_DECLTYPES 处理来自列声明的 DECIMAL 类型,而 PARSE_COLNAMES 允许在查询中使用类型提示。

我们还演示了为 DECIMAL 类型注册自定义转换器,该转换器将字符串值转换为 Python Decimal 对象。

处理 NULL 值

PARSE_DECLTYPES 正确处理 NULL 值,无需转换尝试,从而保持 Python 的 None 作为数据库 NULL。

null_handling.py
import sqlite3
from datetime import date

with sqlite3.connect(':memory:', detect_types=sqlite3.PARSE_DECLTYPES) as conn:
    cursor = conn.cursor()
    
    cursor.execute('''CREATE TABLE tasks
                      (id INTEGER PRIMARY KEY, description TEXT, 
                       due_date DATE, completed DATE)''')
    
    # Insert data with NULL date
    cursor.execute('''INSERT INTO tasks (description, due_date, completed)
                      VALUES (?, ?, ?)''',
                  ('Write report', date(2025, 6, 15), None))
    
    # Retrieve and check NULL handling
    cursor.execute("SELECT due_date, completed FROM tasks")
    due, completed = cursor.fetchone()
    
    print(f"Due date type: {type(due)}")  # datetime.date
    print(f"Completed type: {type(completed)}")  # NoneType
    print(f"Is completed None: {completed is None}")  # True

此示例演示了,即使对于使用 DATE 类型声明的列,类型列中的 NULL 值在检索时仍为 Python None

类型转换仅针对非 NULL 值发生,从而使该行为对于数据库架构中的可空列是安全的。

自定义类型转换

您可以注册自定义转换器,以将 PARSE_DECLTYPES 扩展到您自己的 Python 类型。

custom_converter.py
import sqlite3
import json
from typing import Dict

# Define custom type and converter
def dict_converter(value: bytes) -> Dict:
    return json.loads(value.decode('utf-8'))

# Register the converter
sqlite3.register_converter("JSON", dict_converter)

with sqlite3.connect(':memory:', detect_types=sqlite3.PARSE_DECLTYPES) as conn:
    cursor = conn.cursor()
    
    # Create table with JSON type
    cursor.execute('''CREATE TABLE configs
                      (id INTEGER PRIMARY KEY, name TEXT, settings JSON)''')
    
    # Insert dictionary as JSON
    settings = {'theme': 'dark', 'notifications': True, 'timeout': 30}
    cursor.execute("INSERT INTO configs (name, settings) VALUES (?, ?)",
                  ('User Preferences', json.dumps(settings)))
    
    # Retrieve and verify automatic conversion
    cursor.execute("SELECT settings FROM configs")
    retrieved = cursor.fetchone()[0]
    print(f"Type: {type(retrieved)}")  # dict
    print(f"Theme: {retrieved['theme']}")  # dark
    print(f"Original == Retrieved: {settings == retrieved}")  # True

此示例展示了如何通过注册自定义转换器来处理 SQLite 中的 JSON 数据。 JSON 列类型会自动转换为 Python 字典。

转换器函数接收值作为字节,并且必须返回转换后的 Python 对象。 在这里,我们使用 JSON 序列化进行转换。

使用 Time 值

PARSE_DECLTYPES 也可以将 TIME 列转换为 Python datetime.time 对象。

time_conversion.py
import sqlite3
from datetime import time

with sqlite3.connect(':memory:', detect_types=sqlite3.PARSE_DECLTYPES) as conn:
    cursor = conn.cursor()
    
    # Create table with TIME type
    cursor.execute('''CREATE TABLE schedule
                      (id INTEGER PRIMARY KEY, event TEXT, 
                       start_time TIME, end_time TIME)''')
    
    # Insert time values
    start = time(9, 30)
    end = time(17, 0)
    cursor.execute('''INSERT INTO schedule (event, start_time, end_time)
                      VALUES (?, ?, ?)''',
                  ('Workday', start, end))
    
    # Retrieve and verify times
    cursor.execute("SELECT start_time, end_time FROM schedule")
    retrieved_start, retrieved_end = cursor.fetchone()
    
    print(f"Start type: {type(retrieved_start)}")  # datetime.time
    print(f"End type: {type(retrieved_end)}")  # datetime.time
    print(f"Start matches: {retrieved_start == start}")  # True
    print(f"End matches: {retrieved_end == end}")  # True

此示例演示了将 TIME 列自动转换为 Python datetime.time 对象。 转换保留所有时间组件。

与其他时间类型一样,必须在表架构中将列正确声明为 TIME,以便转换自动工作。

最佳实践

资料来源

作者

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

列出所有 Python 教程