FastAPI + SQLite 原生无主键表 完整增删改查

文章前言

本文基于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

代码说明

  1. 内置全局跨域中间件,解决前端网页跨域请求报错
  2. SQLite 数据库连接配置check_same_thread=False,解决 FastAPI 多线程访问数据库崩溃 500 错误
  3. 全接口try-except异常捕获,服务不会被数据库异常打崩,返回友好报错信息
  4. 100% 适配 Navicat 原生无主键表结构,全程不新增 id 字段 ,以A1Real唯一数值字段作为删除标识
  5. 封装数据库通用连接方法,代码低耦合、易维护
  6. 内置 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}
                    &nbsp;&nbsp;A2文本:${A2}
                    &nbsp;&nbsp;A3文本:${A3}
                    &nbsp;&nbsp;
                    <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 内直接运行(开发调试用)

  1. 将上述main.py代码完整粘贴进 PyCharm 项目文件

  2. 确认项目已加载本地.venv虚拟环境,依赖包齐全

  3. 直接点击 PyCharm 左侧绿色运行三角按钮

  4. 控制台出现如下日志,代表后端服务启动成功: plaintext

    复制代码
    INFO:     Uvicorn running on http://0.0.0.0:8001
  5. 服务保持运行窗口不关闭,直接双击项目文件夹内index.html,即可打开前端页面操作数据库。

方式 2:无需 PyCharm,双击 bat 脚本本地发布运行

后续脱离编程软件,纯本地永久发布运行,制作一键启动脚本:

  1. fast02项目根文件夹空白处,右键新建文本文档
  2. 粘贴以下脚本代码:

bat

复制代码
@echo off
cd /d "%~dp0"
.venv\Scripts\uvicorn.exe main:app --host 0.0.0.0 --port 8001
pause
  1. 将文件重命名为 启动后端.bat(修改后缀.txt.bat
  2. 以后直接双击此 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 是多线程服务,多线程同时操作数据库会直接触发线程冲突崩溃;同时缺少异常捕获,任意数据库语法错误都会直接打崩服务。解决方案

  1. 数据库连接添加参数 check_same_thread=False,关闭 SQLite 线程隔离限制
  2. 所有数据库操作全部包裹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 个核心数据库操作:

  1. 新增:前端填写 3 个字段内容,点击提交,数据写入本地 SQLite 数据库
  2. 全量查询:页面打开自动加载库内所有数据,前端完整渲染展示
  3. 条件删除:点击对应数据的删除按钮,根据 A1Real 字段匹配删除数据库内本条数据

总结

本文完整实现了原生无主键 SQLite 表 + FastAPI 后端 + 原生 HTML 前端的本地增删查全流程项目,完整规避了新手搭建过程中 90% 的高频报错问题。项目纯本地文件部署,无需数据库服务、无需额外框架,开箱即用,同时完整记录了每一个报错根源与解决方案,非常适合 FastAPI 入门、SQLite 本地数据库对接学习。

相关推荐
qq_342295821 小时前
如何备份大量小表组成的数据库_并行导出与多文件并发写入.txt
jvm·数据库·python
justjinji2 小时前
MySQL存储过程中如何防止SQL注入_使用参数化查询规范
jvm·数据库·python
qq_206901392 小时前
mysql索引排序规则设置方法_mysqlCollation对索引影响
jvm·数据库·python
HHHHH1010HHHHH2 小时前
如何快速重置SQL表中的自增ID_使用TRUNCATE与重置命令
jvm·数据库·python
m0_734949792 小时前
html怎么转konva舞台_Konva如何在HTML中创建2D绘图舞台
jvm·数据库·python
m0_716430072 小时前
如何在非受控输入中实时显示值(不依赖状态更新)
jvm·数据库·python
2201_761040592 小时前
如何统计SQL分组汇总数据_详解GROUP BY与HAVING用法
jvm·数据库·python
2301_764150562 小时前
CSS如何通过BEM提升质量_应用命名规范减少Bug产生
jvm·数据库·python