Python sqlite3.PARSE_DECLTYPES Constant
Last modified April 15, 2025
This comprehensive guide explores Python's sqlite3.PARSE_DECLTYPES
constant, which enables automatic type conversion when retrieving data from SQLite
databases. We'll cover its usage, benefits, and practical examples.
Basic Definitions
The sqlite3.PARSE_DECLTYPES
constant is used with sqlite3.connect
to enable type conversion based on column declarations. When set, SQLite will
attempt to convert values to Python types matching the column's declared type.
Key characteristics: it works with the detect_types
parameter,
supports standard Python types like datetime.date
, and requires
proper column type declarations in table definitions.
Basic Usage with PARSE_DECLTYPES
Here's a simple example demonstrating how to use PARSE_DECLTYPES
for automatic type conversion of date values.
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
This example shows how PARSE_DECLTYPES
automatically converts SQLite
DATE values to Python datetime.date
objects. The conversion happens
during data retrieval.
Note the importance of declaring column types properly (DATE in this case) for the conversion to work correctly. Without the type declaration, no conversion would occur.
Working with DATETIME Values
PARSE_DECLTYPES
can also handle datetime values when properly
declared in the table schema.
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}")
This example demonstrates automatic conversion of TIMESTAMP columns to Python
datetime.datetime
objects. The conversion preserves all datetime
components including microseconds.
The key requirement is declaring the column as TIMESTAMP in the table schema. Other declarations like DATETIME would also work for this conversion.
Combining with PARSE_COLNAMES
PARSE_DECLTYPES
can be combined with PARSE_COLNAMES
for
more flexible type conversion scenarios.
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
This example shows how to combine both parsing modes. PARSE_DECLTYPES
handles the DECIMAL type from column declarations, while PARSE_COLNAMES
allows type hints in queries.
We also demonstrate registering a custom converter for the DECIMAL type, which
converts string values to Python Decimal
objects.
Handling NULL Values
PARSE_DECLTYPES
properly handles NULL values without conversion
attempts, maintaining Python's None
for database NULLs.
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
This example demonstrates that NULL values in typed columns remain as Python
None
when retrieved, even for columns declared with DATE type.
The type conversion only occurs for non-NULL values, making the behavior safe for nullable columns in your database schema.
Custom Type Conversion
You can register custom converters to extend PARSE_DECLTYPES
for
your own Python types.
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
This example shows how to handle JSON data in SQLite by registering a custom converter. The JSON column type is automatically converted to Python dictionaries.
The converter function receives the value as bytes and must return the converted Python object. Here we use JSON serialization for the conversion.
Working with Time Values
PARSE_DECLTYPES
can also convert TIME columns to Python
datetime.time
objects.
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
This example demonstrates automatic conversion of TIME columns to Python
datetime.time
objects. The conversion preserves all time components.
As with other temporal types, the column must be properly declared as TIME in the table schema for the conversion to work automatically.
Best Practices
- Always declare column types: PARSE_DECLTYPES relies on proper type declarations
- Register custom converters: Extend functionality for your specific needs
- Handle NULLs properly: Test with nullable columns
- Combine with PARSE_COLNAMES: For maximum flexibility
- Document type conversions: Make the behavior clear in your codebase
Source References
Author
List all Python tutorials.