Python SQLite3 查詢結(jié)果返回字典的常見錯(cuò)誤及完整解決方案
問題背景
在使用 Python 的 sqlite3 模塊查詢數(shù)據(jù)庫時(shí),默認(rèn)情況下 fetchall() 返回的結(jié)果只包含值(tuple 格式),不包含字段名(鍵),這在實(shí)際開發(fā)中很不方便。
默認(rèn)行為示例
import sqlite3
conn = sqlite3.connect('database.db')
cu = conn.cursor()
cu.execute("select id, username, email from user")
result = cu.fetchall()
print(result)
# 輸出: [(1, 'alice', 'alice@example.com'), (2, 'bob', 'bob@example.com')]
# 只有值,沒有字段名解決方案
通過設(shè)置 conn.row_factory = sqlite3.Row 并將結(jié)果轉(zhuǎn)換為字典,可以得到包含字段名的查詢結(jié)果。
完整代碼示例
import sqlite3
from flask import jsonify
def userSearch():
# 連接數(shù)據(jù)庫
conn = handleDbConnection()
# 關(guān)鍵步驟1: 設(shè)置 row_factory
conn.row_factory = sqlite3.Row
# 創(chuàng)建游標(biāo)并執(zhí)行查詢
cu = conn.cursor()
cu.execute("select * from user")
# 獲取查詢結(jié)果
listAll = cu.fetchall()
# 關(guān)鍵步驟2: 將 Row 對(duì)象轉(zhuǎn)換為字典
listAll = [dict(row) for row in listAll]
# 現(xiàn)在 listAll 是字典列表
# [{'id': 1, 'username': 'alice', 'email': 'alice@example.com'}, ...]
return jsonify({
"code": 200,
"data": {
"list": listAll,
"total": len(listAll)
}
})核心語法詳解
1.conn.row_factory = sqlite3.Row
作用: 改變查詢結(jié)果的返回格式
- 默認(rèn)情況:
row_factory = None,返回普通的 tuple(元組) - 設(shè)置后: 返回
sqlite3.Row對(duì)象,支持按列名訪問
Row 對(duì)象的特點(diǎn):
row = cu.fetchone()
# 可以通過索引訪問
print(row[0]) # 第一列的值
# 可以通過列名訪問
print(row['username']) # username 字段的值
# 可以轉(zhuǎn)換為字典
print(dict(row)) # {'id': 1, 'username': 'alice', ...}2.listAll = [dict(row) for row in listAll]
這是 Python 的**列表推導(dǎo)式(List Comprehension)**語法。
語法結(jié)構(gòu)分解
[dict(row) for row in listAll]
完整語法結(jié)構(gòu):
[表達(dá)式 for 變量 in 可迭代對(duì)象]
等價(jià)的傳統(tǒng)寫法:
result = []
for row in listAll:
result.append(dict(row))
listAll = result
執(zhí)行過程詳解
- 遍歷
listAll中的每個(gè)row對(duì)象 - 對(duì)每個(gè)
row執(zhí)行dict(row),將 Row 對(duì)象轉(zhuǎn)換為字典 - 將所有字典收集到一個(gè)新列表中
- 返回新列表
數(shù)據(jù)轉(zhuǎn)換過程
# 轉(zhuǎn)換前(Row 對(duì)象列表)
listAll = [
<sqlite3.Row object at 0x...>,
<sqlite3.Row object at 0x...>
]
# 轉(zhuǎn)換后(字典列表)
listAll = [
{'id': 1, 'username': 'alice', 'email': 'alice@example.com'},
{'id': 2, 'username': 'bob', 'email': 'bob@example.com'}
]列表推導(dǎo)式深入理解
基礎(chǔ)示例
# 示例1: 生成平方數(shù)列表
numbers = [1, 2, 3, 4, 5]
squares = [x**2 for x in numbers]
print(squares) # [1, 4, 9, 16, 25]
# 示例2: 轉(zhuǎn)換為大寫
names = ['alice', 'bob', 'charlie']
upper_names = [name.upper() for name in names]
print(upper_names) # ['ALICE', 'BOB', 'CHARLIE']
# 示例3: 提取字典的某個(gè)字段
users = [
{'name': 'alice', 'age': 25},
{'name': 'bob', 'age': 30}
]
names = [user['name'] for user in users]
print(names) # ['alice', 'bob']帶條件的列表推導(dǎo)式
# 語法:[表達(dá)式 for 變量 in 可迭代對(duì)象 if 條件] # 示例: 只轉(zhuǎn)換偶數(shù) numbers = [1, 2, 3, 4, 5, 6] even_squares = [x**2 for x in numbers if x % 2 == 0] print(even_squares) # [4, 16, 36] # 在 SQLite 場景中的應(yīng)用 listAll = [dict(row) for row in listAll if row['age'] > 18] # 只轉(zhuǎn)換年齡大于18的記錄
嵌套列表推導(dǎo)式
# 示例: 扁平化二維列表 matrix = [[1, 2], [3, 4], [5, 6]] flat = [num for row in matrix for num in row] print(flat) # [1, 2, 3, 4, 5, 6]
為什么需要轉(zhuǎn)換為字典?
1. JSON 序列化兼容性
sqlite3.Row 對(duì)象不能直接被 json.dumps() 或 Flask 的 jsonify() 序列化:
# 錯(cuò)誤示例
listAll = cu.fetchall() # Row 對(duì)象列表
return jsonify({"data": listAll})
# TypeError: Object of type Row is not JSON serializable
# 正確示例
listAll = [dict(row) for row in cu.fetchall()]
return jsonify({"data": listAll})
# 成功序列化為 JSON2. 更好的可讀性和可維護(hù)性
# 使用字典
user = {'id': 1, 'username': 'alice', 'email': 'alice@example.com'}
print(user['username']) # 清晰明了
# 使用 tuple(默認(rèn))
user = (1, 'alice', 'alice@example.com')
print(user[1]) # 需要記住索引位置,容易出錯(cuò)完整最佳實(shí)踐
封裝為工具函數(shù)
import sqlite3
from typing import List, Dict, Any
def query_to_dict(conn: sqlite3.Connection, sql: str, params: tuple = ()) -> List[Dict[str, Any]]:
"""
執(zhí)行 SQL 查詢并返回字典列表
Args:
conn: 數(shù)據(jù)庫連接對(duì)象
sql: SQL 查詢語句
params: 查詢參數(shù)(可選)
Returns:
字典列表,每個(gè)字典代表一行數(shù)據(jù)
"""
# 設(shè)置 row_factory
conn.row_factory = sqlite3.Row
cu = conn.cursor()
# 執(zhí)行查詢
cu.execute(sql, params)
# 轉(zhuǎn)換為字典列表
result = [dict(row) for row in cu.fetchall()]
# 關(guān)閉游標(biāo)
cu.close()
return result
# 使用示例
conn = sqlite3.connect('database.db')
users = query_to_dict(conn, "SELECT * FROM user WHERE age > ?", (18,))
print(users)
# [{'id': 1, 'username': 'alice', 'age': 25}, ...]帶分頁的查詢示例
def userSearch(pageNum: int = 1, pageSize: int = 10) -> dict:
"""
用戶查詢(帶分頁)
Args:
pageNum: 頁碼(從1開始)
pageSize: 每頁數(shù)量
Returns:
包含列表和分頁信息的字典
"""
conn = handleDbConnection()
conn.row_factory = sqlite3.Row
cu = conn.cursor()
# 查詢總數(shù)
cu.execute("SELECT COUNT(*) as total FROM user")
total = cu.fetchone()['total']
# 計(jì)算偏移量
offset = (pageNum - 1) * pageSize
# 查詢分頁數(shù)據(jù)
cu.execute("SELECT * FROM user LIMIT ? OFFSET ?", (pageSize, offset))
listAll = [dict(row) for row in cu.fetchall()]
cu.close()
conn.close()
return {
"code": 200,
"data": {
"list": listAll,
"total": total,
"pageNum": pageNum,
"pageSize": pageSize,
"totalPages": (total + pageSize - 1) // pageSize
},
"message": "成功"
}性能對(duì)比
方法對(duì)比
import time
# 方法1: 使用列表推導(dǎo)式(推薦)
start = time.time()
result1 = [dict(row) for row in listAll]
print(f"列表推導(dǎo)式耗時(shí): {time.time() - start:.4f}秒")
# 方法2: 使用 map 函數(shù)
start = time.time()
result2 = list(map(dict, listAll))
print(f"map 函數(shù)耗時(shí): {time.time() - start:.4f}秒")
# 方法3: 使用傳統(tǒng)循環(huán)
start = time.time()
result3 = []
for row in listAll:
result3.append(dict(row))
print(f"傳統(tǒng)循環(huán)耗時(shí): {time.time() - start:.4f}秒")結(jié)論: 列表推導(dǎo)式通常性能最優(yōu),且代碼最簡潔。
其他高級(jí)用法
1. 自定義字段轉(zhuǎn)換
# 轉(zhuǎn)換時(shí)修改字段名
listAll = [
{
'userId': row['id'],
'userName': row['username'],
'userEmail': row['email']
}
for row in cu.fetchall()
]2. 添加計(jì)算字段
# 添加額外的計(jì)算字段
listAll = [
{
**dict(row), # 展開原始字段
'fullName': f"{row['first_name']} {row['last_name']}",
'isAdmin': row['role'] == 'admin'
}
for row in cu.fetchall()
]
3. 過濾空值
# 只保留非空字段
listAll = [
{k: v for k, v in dict(row).items() if v is not None}
for row in cu.fetchall()
]
常見錯(cuò)誤和解決方案
錯(cuò)誤1: 忘記設(shè)置 row_factory
# 錯(cuò)誤
conn = sqlite3.connect('db.sqlite')
cu = conn.cursor()
cu.execute("SELECT * FROM user")
listAll = [dict(row) for row in cu.fetchall()]
# TypeError: cannot convert dictionary update sequence element #0 to a sequence
# 正確
conn.row_factory = sqlite3.Row # 必須添加這一行
錯(cuò)誤2: 在錯(cuò)誤的位置設(shè)置 row_factory
# 錯(cuò)誤:在 cursor 之后設(shè)置 cu = conn.cursor() conn.row_factory = sqlite3.Row # 太晚了! # 正確:在 cursor 之前設(shè)置 conn.row_factory = sqlite3.Row cu = conn.cursor()
錯(cuò)誤3: Row 對(duì)象直接序列化
# 錯(cuò)誤
return jsonify({"data": cu.fetchall()})
# TypeError: Object of type Row is not JSON serializable
# 正確
return jsonify({"data": [dict(row) for row in cu.fetchall()]})
總結(jié)
核心要點(diǎn)
設(shè)置
row_factory:使查詢結(jié)果支持字典操作conn.row_factory = sqlite3.Row
轉(zhuǎn)換為字典列表:確保 JSON 序列化兼容性
listAll = [dict(row) for row in cu.fetchall()]
列表推導(dǎo)式語法:簡潔高效的數(shù)據(jù)轉(zhuǎn)換方式
[表達(dá)式 for 變量 in 可迭代對(duì)象]
最佳實(shí)踐檢查清單
- ? 在創(chuàng)建 cursor 之前設(shè)置
row_factory - ? 使用列表推導(dǎo)式轉(zhuǎn)換為字典
- ? 封裝常用操作為工具函數(shù)
- ? 及時(shí)關(guān)閉 cursor 和 connection
- ? 使用參數(shù)化查詢防止 SQL 注入
- ? 添加異常處理和日志記錄
參考資源
作者: [你的名字] 日期: 2026-02-10 標(biāo)簽: Python, SQLite3, 數(shù)據(jù)庫, 列表推導(dǎo)式, Flask
到此這篇關(guān)于Python SQLite3 查詢結(jié)果返回字典的常見錯(cuò)誤及完整解決方案的文章就介紹到這了,更多相關(guān)Python SQLite3 查詢結(jié)果返回字典內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Python?time時(shí)間格式化和設(shè)置時(shí)區(qū)實(shí)現(xiàn)代碼詳解
這篇文章主要介紹了Python?time時(shí)間格式化和設(shè)置時(shí)區(qū)實(shí)現(xiàn)代碼,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值2023-02-02
Python如何在一個(gè)類里面調(diào)用另一個(gè)類里面的內(nèi)容
Python 是一門強(qiáng)大且靈活的編程語言,然而我們經(jīng)常會(huì)遇到這樣一個(gè)問題:如何在一個(gè)類里面調(diào)用另一個(gè)類里面的東西,下面我們就來看看幾種實(shí)現(xiàn)方法吧2025-09-09
python如何實(shí)現(xiàn)單向鏈表及單向鏈表的反轉(zhuǎn)
這篇文章主要介紹了python如何實(shí)現(xiàn)單向鏈表及單向鏈表的反轉(zhuǎn),幫助大家更好的理解和學(xué)習(xí)使用python,感興趣的朋友可以了解下2021-03-03
python 去除二維數(shù)組/二維列表中的重復(fù)行方法
今天小編就為大家分享一篇python 去除二維數(shù)組/二維列表中的重復(fù)行方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2019-01-01
Python庫學(xué)習(xí)Tkinter制作GUI個(gè)性簽名設(shè)計(jì)軟件
Tkinter 是 Python 中的標(biāo)準(zhǔn) GUI 庫,使用 Tkinter 可以快速地創(chuàng)建 GUI 應(yīng)用程序。今天我們打算再用一個(gè)小案例,帶大家加深對(duì)Tkinter的理解2021-09-09

