文章前言

本文基于Navicat 创建的 SQLite 原生数据表(仅含 3 业务字段、无自增 ID 主键) ,从零搭建一套完整的 FastAPI 后端接口 + HTML 原生前端页面,实现数据新增、全量查询、条件删除 全套业务功能。全程踩坑复盘完整记录:包含 SQLite 多线程报错、表字段不匹配no such column、FastAPI 内部 500 错误、跨域访问、前端接口请求异常等新手高频问题,代码开箱即用,本地直接部署运行,无需修改环境配置。
项目环境
-
后端框架:
FastAPI -
数据库:
SQLite(轻量本地文件数据库,无需额外安装服务) -
前端:原生 HTML+JS,无需 Vue/React 框架
-
运行环境:Windows、Python 虚拟环境
-
数据库原生表结构(Navicat 直接创建): 表格
字段名 字段类型 说明 A1Real REAL 浮点数字段 A2String TEXT 文本字段 A3String TEXT 文本字段
重点注意:原生数据表无 id 自增主键,这也是本次开发绝大多数报错的根源,网上绝大多数教程默认数据表自带 id 主键,直接套用会全部报错。
一、项目目录结构
全部项目文件存放路径:D:\project\04 pyhton\fast02\
plaintext
fast02
├─ .venv # Python虚拟环境(已安装fastapi、uvicorn依赖)
├─ 新建文本文档.db # SQLite本地数据库文件(Navicat已建好ABCTable数据表)
├─ main.py # FastAPI后端接口源码
└─ index.html # 原生前端操作页面
二、后端完整源码 main.py
代码说明
- 内置全局跨域中间件,解决前端网页跨域请求报错
- SQLite 数据库连接配置
check_same_thread=False,解决 FastAPI 多线程访问数据库崩溃 500 错误 - 全接口
try-except异常捕获,服务不会被数据库异常打崩,返回友好报错信息 - 100% 适配 Navicat 原生无主键表结构,全程不新增 id 字段 ,以
A1Real唯一数值字段作为删除标识 - 封装数据库通用连接方法,代码低耦合、易维护
- 内置 PyCharm 一键运行入口,同时支持 bat 脚本本地无编辑器启动
python
运行
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
import sqlite3
# 初始化FastAPI应用
app = FastAPI()
# 全局跨域配置:允许所有来源、请求方式、请求头,解决前后端跨域问题
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# ===================== 数据库配置 =====================
# 本地SQLite数据库文件路径
DB_PATH = "新建文本文档.db"
# ===================== 数据模型 =====================
# Pydantic数据校验模型,严格匹配Navicat原生表的3个业务字段
# 无id字段,完全贴合原始数据表结构
class Item(BaseModel):
A1Real: float
A2String: str
A3String: str
# ===================== 数据库通用工具函数 =====================
def get_db_conn():
"""
统一封装SQLite数据库连接
check_same_thread=False:解决FastAPI多线程环境下SQLite默认线程隔离报错
"""
conn = sqlite3.connect(DB_PATH, check_same_thread=False)
return conn
# ===================== 数据表初始化 =====================
def init_table():
"""
程序启动自动检查表,表不存在则创建
结构完全复刻Navicat原生ABCTable,无id主键,仅保留3个业务字段
"""
try:
conn = get_db_conn()
cursor = conn.cursor()
cursor.execute('''
CREATE TABLE IF NOT EXISTS ABCTable (
A1Real REAL,
A2String TEXT,
A3String TEXT
)
''')
conn.commit()
conn.close()
except Exception as e:
print(f"数据表初始化异常:{e}")
# 程序启动自动执行建表
init_table()
# ===================== 接口1:新增数据 =====================
@app.post("/add")
def add_data(item: Item):
"""新增数据接口"""
try:
conn = get_db_conn()
cursor = conn.cursor()
# 参数化插入,防止SQL注入
cursor.execute(
"INSERT INTO ABCTable (A1Real,A2String,A3String) VALUES (?,?,?)",
(item.A1Real, item.A2String, item.A3String)
)
conn.commit()
conn.close()
return {"msg": "数据添加成功"}
except Exception as e:
return {"msg": f"添加失败:{str(e)}"}
# ===================== 接口2:查询全部数据 =====================
@app.get("/list")
def get_all():
"""查询数据库内所有数据接口"""
try:
conn = get_db_conn()
cursor = conn.cursor()
cursor.execute("SELECT * FROM ABCTable")
res = cursor.fetchall()
conn.close()
return {"data": res}
except Exception as e:
return {"data": [], "msg": f"查询失败:{str(e)}"}
# ===================== 接口3:根据A1Real删除数据 =====================
@app.delete("/delete/{A1Real}")
def del_data(A1Real: float):
"""
删除数据接口
适配无id表结构,使用唯一数值字段A1Real作为删除匹配条件
"""
try:
conn = get_db_conn()
cursor = conn.cursor()
cursor.execute("DELETE FROM ABCTable WHERE A1Real = ?", (A1Real,))
conn.commit()
conn.close()
return {"msg": "数据删除成功"}
except Exception as e:
return {"msg": f"删除失败:{str(e)}"}
# ===================== PyCharm一键运行入口 =====================
if __name__ == "__main__":
import uvicorn
# 监听0.0.0.0:本地电脑+同局域网设备均可访问,端口8001
uvicorn.run(app, host="0.0.0.0", port=8001)
三、前端完整源码 index.html
原生 HTML 页面,无需任何框架,双击直接打开浏览器运行,实现数据填写提交、全量数据展示、一键删除全部功能,自动适配后端接口,空值异常兜底展示。
html
预览
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>SQLite数据库管理系统</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: "微软雅黑", sans-serif;
}
body {
padding: 40px;
background-color: #f0f2f5;
}
.box {
background: #ffffff;
padding: 25px;
border-radius: 10px;
margin-bottom: 20px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
}
.box h3 {
margin-bottom: 15px;
color: #333;
}
input {
width: 300px;
padding: 10px 12px;
margin: 8px 0;
border: 1px solid #ccc;
border-radius: 5px;
display: block;
}
button {
padding: 9px 18px;
border: none;
border-radius: 5px;
cursor: pointer;
margin-right: 10px;
font-size: 14px;
}
.btn-add {
background: #165DFF;
color: #fff;
}
.btn-refresh {
background: #4080FF;
color: #fff;
}
.btn-del {
background: #F53F3F;
color: #fff;
}
.item {
padding: 10px 0;
border-bottom: 1px solid #eeeeee;
}
</style>
</head>
<body>
<!-- 数据新增表单区域 -->
<div class="box">
<h3>新增数据</h3>
<input type="number" id="a1" placeholder="A1Real 数字(唯一标识)">
<input id="a2" placeholder="A2String 文本内容">
<input id="a3" placeholder="A3String 文本内容">
<button class="btn-add" onclick="addData()">提交添加</button>
<button class="btn-refresh" onclick="loadAllData()">刷新列表</button>
</div>
<!-- 数据列表展示区域 -->
<div class="box">
<h3>数据库全部数据</h3>
<div id="dataList"></div>
</div>
<script>
// 后端FastAPI接口根地址
const baseApiUrl = "http://127.0.0.1:8001";
// 1. 新增数据接口请求
async function addData() {
// 获取页面输入框内容
const A1Real = parseFloat(document.getElementById('a1').value);
const A2String = document.getElementById('a2').value;
const A3String = document.getElementById('a3').value;
// 调用后端新增接口
const response = await fetch(baseApiUrl + "/add", {
method: "POST",
headers: {"Content-Type": "application/json"},
body: JSON.stringify({A1Real, A2String, A3String})
});
const result = await response.json();
alert(result.msg);
// 添加成功后自动刷新数据列表
loadAllData();
}
// 2. 查询加载全部数据
async function loadAllData() {
const response = await fetch(baseApiUrl + "/list");
const json = await response.json();
let htmlStr = "";
// 遍历后端返回数据,渲染页面
json.data.forEach(row => {
// 空值兜底,避免页面undefined异常
const A1 = row[0] ?? "空";
const A2 = row[1] ?? "空";
const A3 = row[2] ?? "空";
htmlStr += `
<div class="item">
A1数值:${A1}
A2文本:${A2}
A3文本:${A3}
<button class="btn-del" onclick="deleteData(${A1})">删除本条</button>
</div>
`;
});
document.getElementById('dataList').innerHTML = htmlStr;
}
// 3. 根据A1Real删除数据
async function deleteData(A1Real) {
const response = await fetch(baseApiUrl + "/delete/" + A1Real, {
method: "DELETE",
headers: {"Content-Type": "application/json"}
});
const result = await response.json();
alert(result.msg);
// 删除成功后自动刷新列表
loadAllData();
}
// 页面加载完成自动渲染数据库数据
window.onload = loadAllData;
</script>
</body>
</html>
四、本地运行部署教程(2 种方式)
方式 1:PyCharm 内直接运行(开发调试用)
-
将上述
main.py代码完整粘贴进 PyCharm 项目文件 -
确认项目已加载本地
.venv虚拟环境,依赖包齐全 -
直接点击 PyCharm 左侧绿色运行三角按钮
-
控制台出现如下日志,代表后端服务启动成功: plaintext
INFO: Uvicorn running on http://0.0.0.0:8001 -
服务保持运行窗口不关闭,直接双击项目文件夹内
index.html,即可打开前端页面操作数据库。
方式 2:无需 PyCharm,双击 bat 脚本本地发布运行
后续脱离编程软件,纯本地永久发布运行,制作一键启动脚本:
- 在
fast02项目根文件夹空白处,右键新建文本文档 - 粘贴以下脚本代码:
bat
@echo off
cd /d "%~dp0"
.venv\Scripts\uvicorn.exe main:app --host 0.0.0.0 --port 8001
pause
- 将文件重命名为
启动后端.bat(修改后缀.txt为.bat) - 以后直接双击此 bat 文件,即可自动启动 FastAPI 本地接口,全程无需打开 PyCharm、无需敲任何命令。
接口文档访问
FastAPI 自带自动生成接口调试文档,浏览器打开地址:
plaintext
http://127.0.0.1:8001/docs
可在线直接测试新增、查询、删除全部接口。
五、本次开发全程踩坑复盘(新手必看)
坑 1:no such column: id 字段不存在报错
报错原因 :网上绝大多数 FastAPI+SQLite 教程默认数据表自带id自增主键,但本次 Navicat 原生创建的表完全没有 id 字段 ,代码强行通过 id 进行删除、查询操作,直接触发字段缺失报错。解决方案 :全程剔除 id 相关代码,严格贴合原始数据表结构,选用表内唯一数值字段A1Real作为数据唯一标识,进行删除匹配。
坑 2:FastAPI 接口返回 500 Internal Server Error 服务器内部错误
报错原因 :SQLite 原生默认单线程,而 FastAPI 的 uvicorn 是多线程服务,多线程同时操作数据库会直接触发线程冲突崩溃;同时缺少异常捕获,任意数据库语法错误都会直接打崩服务。解决方案:
- 数据库连接添加参数
check_same_thread=False,关闭 SQLite 线程隔离限制 - 所有数据库操作全部包裹
try-except异常捕获,拦截所有异常,返回友好提示,服务不会崩溃。
坑 3:浏览器无法访问 0.0.0.0 地址
原因 :0.0.0.0 是服务监听地址 ,作用是放开本机所有网卡访问权限,不是浏览器可访问地址 。正确访问地址 :http://127.0.0.1:8001
坑 4:前端删除按钮点击无反应
原因 :初始 DELETE 请求缺少标准请求头、后端接口参数不匹配、数据库异常未返回结果。解决方案 :补全 fetch 请求头Content-Type: application/json,后端完善异常返回,前端接收返回结果后自动刷新页面。
坑 5:前端页面数据展示undefined空值异常
原因 :数据库字段为空时,JS 直接取值会显示 undefined。解决方案 :添加空值兜底语法 ?? "空",为空时统一展示自定义文字。
坑 6:前后端跨域请求拦截报错
解决方案 :FastAPI 添加CORSMiddleware跨域中间件,配置全部放行,解决浏览器同源策略限制。
六、功能测试说明
项目完整实现 3 个核心数据库操作:
- 新增:前端填写 3 个字段内容,点击提交,数据写入本地 SQLite 数据库
- 全量查询:页面打开自动加载库内所有数据,前端完整渲染展示
- 条件删除:点击对应数据的删除按钮,根据 A1Real 字段匹配删除数据库内本条数据
总结
本文完整实现了原生无主键 SQLite 表 + FastAPI 后端 + 原生 HTML 前端的本地增删查全流程项目,完整规避了新手搭建过程中 90% 的高频报错问题。项目纯本地文件部署,无需数据库服务、无需额外框架,开箱即用,同时完整记录了每一个报错根源与解决方案,非常适合 FastAPI 入门、SQLite 本地数据库对接学习。