前端转后端生存指南(中):化身架构师,用 ORM 魔法掌控数据库

前端转后端生存指南(中):化身架构师,用 ORM 魔法掌控数据库

在上一篇中,我们打破了面对空白 Python 文件的恐惧,成功跑通了 FastAPI 的第一层脚手架,并学会了如何优雅地读取 .env 配置。

但作为一个数字孪生/3D项目,不管是堆垛机的坐标,还是传送带的状态,如果在前端,页面一刷新数据就全丢了(因为它们活在内存或 Pinia 里)。但在后端,我们的核心使命是:把数据安全、持久地存进硬盘里。

这一篇,我们将化身架构师,接入 MySQL 数据库。我们将使用后端的终极黑魔法------ORM(对象关系映射) 。学会它,你甚至不需要写一行难懂的 SQL 语句,就能像操作 JS 对象一样操纵数据库。

概念映射:保安与库管员的协同

在写代码之前,我们要先跨越所有前端转后端都会遇到的第一个"认知门槛":为什么数据流转需要两套不同的模型?

前端 / 现实世界 FastAPI 后端对应概念 它的核心职责
TypeScript interface / 门口保安 schemas.py (Pydantic 模型) 校验外部输入。它负责检查前端发来的 JSON 对不对,有没有少传字段。只有它放行的数据,才会进入系统。
数据库表结构 / 库管员 models.py (SQLAlchemy 模型) 管理内部存储。它只关心数据库里真实的"货架"长什么样,负责把数据真正写入物理硬盘。

理解了这个分工,后面的增删改查就只是一场"前端发请求 -> 保安校验 -> 库管库存库"的接力赛。

一、 准备材料:安装依赖与拼装"数据库钥匙"

1. 安装 ORM 与 MySQL 驱动 在终端运行以下命令。这就好比你在 Node.js 里安装 mysql2

csharp 复制代码
uv add sqlalchemy pymysql

2. 在 .env 中添加数据库信息 打开你的 .env 文件,补充以下真实信息:

ini 复制代码
MYSQL_HOST=localhost
MYSQL_PORT=3306
MYSQL_USER=root
MYSQL_PASSWORD=123
MYSQL_DATABASE=3d_mqtt

(注意:请确保你已经在本地 MySQL 里提前建好了 3d_mqtt 这个空数据库!)

3. 在 config.py 中拼装连接字符串 我们需要一个极长的字符串来连接数据库,别自己拼,让程序自动算出来:

python 复制代码
# config.py
from pydantic_settings import BaseSettings, SettingsConfigDict

class Settings(BaseSettings):
    app_name: str
    nano_mq_url: str
    
    # 声明从 .env 读取的 MySQL 配置
    mysql_host: str
    mysql_port: int
    mysql_user: str
    mysql_password: str
    mysql_database: str

    # 【魔法属性】:自动拼装出 sqlalchemy 要求的格式
    # 比如:mysql+pymysql://root:123@localhost:3306/3d_mqtt
    @property
    def database_url(self) -> str:
        return f"mysql+pymysql://{self.mysql_user}:{self.mysql_password}@{self.mysql_host}:{self.mysql_port}/{self.mysql_database}"
    
    model_config = SettingsConfigDict(env_file=".env", env_file_encoding="utf-8")

settings = Settings()

二、 搭建基建:创建数据库引擎与图纸

我们需要创建一个专门的 database.py,把这里想象成一个"建筑工地司令部"。

ini 复制代码
# database.py
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker, declarative_base
from config import settings

# 1. 创建引擎 (雇佣一支能连接数据库的施工队)
# echo=True 是新手神器!它会把底层偷偷执行的 SQL 语句全打印在终端里。
engine = create_engine(settings.database_url, echo=True)

# 2. 创建会话工厂 (造一把能够打开数据库大门的万能钥匙工厂)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)

# 3. 创建基类 (创建一个图纸总文件夹)
Base = declarative_base()

接下来,我们来画真实的表结构图纸。新建 models.py

sql 复制代码
# models.py
from sqlalchemy import Column, Integer, String, Float
from database import Base

# 定义堆垛机 (Stacker) 在数据库里的样子。只要继承了 Base,就会被放进图纸总文件夹。
class Stacker(Base):
    __tablename__ = "stacker"  # 数据库里的真实表名

    id = Column(Integer, primary_key=True, index=True, autoincrement=True)
    device_id = Column(String(50), unique=True, index=True, comment="设备编号")
    status = Column(String(20), comment="当前状态: running/idle/error")
    current_x = Column(Float, default=0.0, comment="当前X坐标")
    current_y = Column(Float, default=0.0, comment="当前Y坐标")

最后,回到我们的主入口 main.py,给施工队下达终极建表命令

python 复制代码
# main.py 的开头补充这些:
from fastapi import FastAPI
from database import engine
import models

# 【终极魔法】:FastAPI 启动前,查阅图纸文件夹,把没建的表全都自动建好!
models.Base.metadata.create_all(bind=engine)

app = FastAPI()
# ... 下面是你之前写的 /health 接口 ...

💡 避坑指南: 施工队有个坏脾气,它只建新楼,绝不改旧楼 。如果你以后在 models.py 里加了新字段,最简单的办法就是去数据库里手动把 stacker 表删掉,然后重启服务让它重建。(企业级项目会使用 Alembic 工具来管理版本,但本地开发直接删表最爽快)。

三、 编写校验规则:门口的"保安"

新建 schemas.py,我们来定义前端应该怎么传数据。这里全是 Pydantic 的代码,非常像 TS 的 interface

python 复制代码
# schemas.py
from pydantic import BaseModel
from typing import Optional

# 这是针对前端发来 POST 请求 (新增设备) 的格式校验
class StackerCreate(BaseModel):
    device_id: str                   # 必填项
    status: Optional[str] = "idle"   # 选填项,默认值
    current_x: Optional[float] = 0.0
    current_y: Optional[float] = 0.0

# 这是针对 PUT 请求 (修改设备) 的格式校验
class StackerUpdate(BaseModel):
    status: Optional[str] = None
    current_x: Optional[float] = None
    current_y: Optional[float] = None

四、 增删改查 (CRUD) 全家桶

万事俱备!回到 main.py,我们将保安(schemas)、库管(models)和钥匙(db)组合在一起。

1. 准备自动借还钥匙的机制:

python 复制代码
# 修改 main.py 的引入
from fastapi import FastAPI, Depends, HTTPException
from sqlalchemy.orm import Session
from database import SessionLocal, engine
import models, schemas

# ... (保留上面的 app 实例化和建表代码) ...

# 每次接口被调用时,临时借一把钥匙;用完后(finally)必须归还!
def get_db():
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()

2. 编写四个核心接口: 将下面这段代码贴在 main.py 的底部:

python 复制代码
# 1. 增 (Create)
@app.post("/stackers")
def create_stacker(stacker_data: schemas.StackerCreate, db: Session = Depends(get_db)):
    # 检查是否有重复设备
    db_stacker = db.query(models.Stacker).filter(models.Stacker.device_id == stacker_data.device_id).first()
    if db_stacker:
        raise HTTPException(status_code=400, detail="该设备号已存在!")
    
    # 包装成数据库模型并入库
    new_stacker = models.Stacker(**stacker_data.model_dump()) # 相当于解构赋值
    db.add(new_stacker)
    db.commit()             # 提交保存
    db.refresh(new_stacker) # 获取数据库生成的 ID
    return {"message": "创建成功", "data": new_stacker}

# 2. 查 (Read) - 获取所有列表
@app.get("/stackers")
def get_stackers(db: Session = Depends(get_db)):
    stackers = db.query(models.Stacker).all()
    return {"data": stackers}

# 3. 改 (Update)
@app.put("/stackers/{stacker_id}")
def update_stacker(stacker_id: int, update_data: schemas.StackerUpdate, db: Session = Depends(get_db)):
    db_stacker = db.query(models.Stacker).filter(models.Stacker.id == stacker_id).first()
    if not db_stacker:
        raise HTTPException(status_code=404, detail="找不到这个设备!")
    
    # 遍历更新有变化的值
    for key, value in update_data.model_dump(exclude_unset=True).items():
        setattr(db_stacker, key, value)
        
    db.commit()
    db.refresh(db_stacker)
    return {"message": "更新成功", "data": db_stacker}

# 4. 删 (Delete)
@app.delete("/stackers/{stacker_id}")
def delete_stacker(stacker_id: int, db: Session = Depends(get_db)):
    db_stacker = db.query(models.Stacker).filter(models.Stacker.id == stacker_id).first()
    if not db_stacker:
        raise HTTPException(status_code=404, detail="找不到这个设备!")
        
    db.delete(db_stacker)
    db.commit()
    return {"message": "删除成功"}

五、 见证奇迹:告别 Postman

代码写完了!在终端运行:

arduino 复制代码
uv run fastapi dev main.py

(注意看终端,如果这是第一次运行,你能清晰地看到黄色的一长串 CREATE TABLE stacker...,说明建表成功了!)

重点来了: 不要去折腾 Postman。直接打开浏览器访问:http://127.0.0.1:8000/docs

你面前会出现 FastAPI 自动为你生成的 Swagger UI 交互式文档。你的四个接口整齐地排列在上面。

  1. 点击 POST /stackers -> 点击 Try it out
  2. 在 JSON 框里修改一下 device_id,点击 Execute
  3. 返回 200!你去数据库里看一眼,数据已经真真切切地存进了你的电脑硬盘里。

总结

恭喜!到这里,你已经掌握了传统后端开发 80% 的日常工作:写接口、连数据库、增删改查。

但作为数字孪生项目,设备坐标怎么能靠人手敲接口来更新呢?下一篇,我们将迎来最终的 Boss 战:引入 MQTT,让设备自动发报,后台接线员自动拦截并存入数据库!

相关推荐
Master_Azur1 小时前
JavaEE之文件操作 字符集 IO流
后端
传说之后1 小时前
GO 语言单元测试入门
后端
古城小栈1 小时前
Bun从Zig迁移至Rust:有何重大意义?
开发语言·后端·rust
༒࿈南林࿈༒1 小时前
某川数据接口逆向、SM系列国密算法
python·js逆向·国密(sm系列)
虎子_layor1 小时前
给 Agent 接入新模型的推理模式:从配置开关到协议适配
后端·架构
ftpeak1 小时前
LangGraph Agent 开发指南(10~子图 Subgraphs)
python·ai·langchain·ai编程·langgraph
财经资讯数据_灵砚智能1 小时前
基于全球经济类多源新闻的NLP情感分析与数据可视化(日间)2026年5月16日
大数据·人工智能·python·信息可视化·自然语言处理
IT_陈寒1 小时前
Java的Stream.peek()千万别乱用,血泪教训
前端·人工智能·后端
Gerardisite1 小时前
企业微信怎么玩?用 API 打造智能私域助手
开发语言·python·机器人·企业微信