前端转后端生存指南(中):化身架构师,用 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,让设备自动发报,后台接线员自动拦截并存入数据库!

相关推荐
上进小菜猪8 小时前
把 KES 集群交给 Kubernetes 的九十天:一个 DBA 的试用实录
后端
两点王爷8 小时前
PostgreSQL 常用 SQL 语句与 GIS 相关函数详解
数据库·后端
麻雀飞吧9 小时前
先判断工具用来学习、开发还是执行
人工智能·python
考虑考虑9 小时前
Springboot环境变量占位符语法
spring boot·后端·spring
人邮异步社区9 小时前
学习Python的最佳学习路径是什么?
python·程序员
步行cgn11 小时前
Spring 基于 XML 的自动装配:byName 详解
java·后端·spring
troy12811 小时前
Python 基础语法(九):Django/Flask/FastAPI 三大 Web 框架详细对比解析
python·jupyter·django·flask·github·fastapi
jzshmyt11 小时前
我用 Python 从零“生成“了一个宇宙,然后让它观察自己(v14)
人工智能·pytorch·python·numpy·matplotlib·空间计算·scipy
RISCV_Explorer13 小时前
RISC-V启动与运行时规范机制解析——BRS约定、HSM多核启动与设备树要求
后端·risc-v
星栈独行13 小时前
ADK-Rust 是什么?Rust 生态新一代 AI Agent 开发套件
人工智能·后端·rust