Python sqlite3组件对比Web应用开发 触发NoneType不可下标错误排查
问题根因拆解
- 触发
TypeError: 'NoneType' object is not subscriptable的直接原因是sqlite3.execute()调用时漏写参数分隔逗号:原代码中两处价格查询的语句把SQL字符串和传参元组直接拼接为函数调用,完全没有触发sqlite的参数绑定逻辑,导致查询匹配不到任何数据库行,fetchone()返回None,对None做下标取值时直接触发类型错误。 - 线上无报错、页面仅显示基础结构的原因:CGI服务运行时,Python脚本一旦触发未捕获异常,会立即终止后续执行逻辑,仅把异常发生前已经生成的HTML片段(也就是页面头部、容器骨架)返回给前端;服务端默认不会把Python异常栈透传到前端,因此会出现“无报错、无对比内容、只有基础body”的现象。
- 隐含的逻辑漏洞:当前代码没有做入参和查询结果的校验,哪怕修复了语法漏逗号的问题,只要用户传了不存在的硬件型号、或者表单未提交对应字段值,同样会触发空值查询、None下标报错。
- 附加的代码问题:同一条查询在
Processeur表上重复执行8次取单值,存在冗余;compareProc函数内部已经打印了完整HTML,外部又对无返回值的函数做嵌套print,会在页面末尾输出多余的None文本,破坏HTML结构。
落地修复方案
- 第一步:修正SQL调用的语法错误
给两处价格查询的SQL语句和传参元组之间补上逗号,保证参数绑定逻辑正常触发:# 修正前(缺逗号,语法错误) # aprixProc1 = connexion.execute('SELECT prixProc FROM Processeur WHERE nomProc=(?);'(processeur1,)).fetchone()[0] # aprixProc2 = connexion.execute("SELECT prixProc FROM Processeur WHERE nomProc=(?);" (processeur2,)).fetchone()[0] # 修正后 aprixProc1 = connexion.execute('SELECT prixProc FROM Processeur WHERE nomProc=(?);', (processeur1,)).fetchone()[0] aprixProc2 = connexion.execute("SELECT prixProc FROM Processeur WHERE nomProc=(?);", (processeur2,)).fetchone()[0] - 第二步:加入全局错误显示开关,解决线上静默失败的问题
在脚本最开头引入CGI的错误显示组件,脚本运行时的所有异常会直接渲染到浏览器页面,不会再出现无报错、无内容的情况:import sqlite3 import cgi import cgitb # 开启前端错误栈显示 cgitb.enable() - 第三步:加入入参和查询结果的容错校验
避免空值、无效型号触发None下标报错,同时把重复的8次单值查询合并为2次全字段查询,减少冗余和出错概率:formulaire = cgi.FieldStorage() processeur1 = formulaire.getvalue('proc1') processeur2 = formulaire.getvalue('proc2') # 校验表单提交值 if not processeur1 or not processeur2: print(pagedebut + "<p style='color:red'>请先选择需要对比的两个硬件型号</p>" + pagefin) connexion.close() exit() # 封装通用的硬件信息查询逻辑 def fetch_proc_info(nom_proc): # 一次性查询所有需要的字段,替代之前8次重复单值查询 sql = """SELECT prixProc, coeursProc, threadsProc, gravProc FROM Processeur WHERE nomProc = ?""" result = connexion.execute(sql, (nom_proc,)).fetchone() if not result: print(pagedebut + f"<p style='color:red'>数据库中未找到型号为「{nom_proc}」的硬件信息</p>" + pagefin) connexion.close() exit() return result # 一次拿全两个处理器的所有属性 proc1 = fetch_proc_info(processeur1) proc2 = fetch_proc_info(processeur2) # 赋值给原有变量,兼容后续对比逻辑 aprixProc1, anbCoeurs1, athreadsProc1, agravProc1 = proc1 aprixProc2, anbCoeurs2, athreadsProc2, agravProc2 = proc2 - 第四步:修复函数的多余输出问题
把compareProc函数内部的打印逻辑改为返回拼接好的HTML字符串,去掉外部嵌套的多余print调用,避免页面末尾出现多余的None文本:def compareProc(prixProc1, prixProc2, nbCoeurs1, nbCoeurs2, threadsProc1, threadsProc2, gravProc1, gravProc2): reponse = '<div class="result-area">' # 原有对比逻辑可按需调整展示文案,避免原来纯数字拼接难以阅读 if prixProc1 < prixProc2: reponse += f"<p>价格更低的硬件:{prixProc1}</p>" else: reponse += f"<p>价格更低的硬件:{prixProc2}</p>" if nbCoeurs1 > nbCoeurs2: reponse += f"<p>核心数更多的硬件:{nbCoeurs1}核</p>" else: reponse += f"<p>核心数更多的硬件:{nbCoeurs2}核</p>" if threadsProc1 > threadsProc2: reponse += f"<p>线程数更多的硬件:{threadsProc1}线程</p>" else: reponse += f"<p>线程数更多的硬件:{threadsProc2}线程</p>" if gravProc1 < gravProc2: reponse += f"<p>制程更先进的硬件:{gravProc1}nm</p>" else: reponse += f"<p>制程更先进的硬件:{gravProc2}nm</p>" reponse += '</div>' # 拼接完整页面返回 return pagedebut + reponse + pagefin # 直接输出最终HTML即可,不需要嵌套print print(compareProc(aprixProc1, aprixProc2, anbCoeurs1, anbCoeurs2, athreadsProc1, athreadsProc2, agravProc1, agravProc2)) connexion.close()
内容的提问来源于stack exchange,提问作者VorTekS
相关产品推荐
相关产品推荐

