这篇文章是一份关于 FastAPI + SQLModel + PostgreSQL + React 全栈项目的环境搭建与初始化指南。它详细介绍了从数据库部署、后端环境配置到前端启动的整个流程,并提供了标准化的项目目录结构。
数据库环境搭建 (Database)
使用 Docker 快速部署: 配置 Docker 镜像源以加速下载。 使用docker run命令启动PostgreSQL数据库服务,并通过数据卷(Volume)实现数据持久化。 部署pgAdmin4作为可视化管理工具,并提供了访问地址和默认登录信息。 连接信息:汇总了连接 PostgreSQL 所需的主机、端口、用户名和密码。
PostgreSQL Server
docker pull postgres:alpine
docker volume create postgre-data
docker run -id --name=postgresql -v postgre-data:"/github/postgresql/data" -p 5432:5432 -e POSTGRES_PASSWORD=123456 -e LANG=C.UTF-8 postgres
PostgreSQL Client 数据库管理客户端
docker pull dpage/pgadmin4
docker run -p 8083:80 -e PGADMIN_DEFAULT_EMAIL=user@qq.com -e PGADMIN_DEFAULT_PASSWORD=123456 -d dpage/pgadmin4
## OK
主机 宿主机 IP(如 127.0.0.1)
端口 5432
用户名 postgres
密码 123456
数据库名 postgres
访问数据库管理工具: http://localhost:8083/browser/ 创建一个名为 hotel 的表
数据库连接文件
文件: core\database\db.py
from contextlib import asynccontextmanager
from sqlmodel.ext.asyncio.session import AsyncSession
from sqlalchemy.ext.asyncio import create_async_engine
SQLALCHEMY_DATABASE_URI="postgresql://postgres:123456@localhost:5432/hotel"
SQLALCHEMY_DATABASE_URI_ASYNC="postgresql+asyncpg://postgres:123456@localhost:5432/hotel"
async_engine = create_async_engine(
SQLALCHEMY_DATABASE_URI_ASYNC,
echo=True,
pool_size=5,
)
# @asynccontextmanager 是 Python 标准库 contextlib 模块提供的装饰器,用于快速创建异步的上下文管理器,核心价值是:
# get_async_session() 必须是一个异步上下文管理器,
@asynccontextmanager
async def get_async_session():
async_session = AsyncSession(async_engine, expire_on_commit=False)
try:
yield async_session
await async_session.commit()
except Exception as e:
await async_session.rollback()
raise e
finally:
await async_session.close()
后端环境配置 (Python FastAPI)
Anaconda 与 Conda 源:
可使用 Anaconda 进行 Python 环境管理,并配置了清华镜像源以加速包的安装。
虚拟环境 (venv):
使用 Python 内置的venv模块创建并激活名为corepy的虚拟环境。 使用venv创建虚拟环境:Python标准库自带,更为简洁
安装了项目核心依赖:fastapi, uvicorn, sqlmodel等。 使用venv创建虚拟环境:Python标准库自带,不可指定python版本
python -m venv corepy
corepy\Scripts\activate
pip install fastapi
pip install starlette
pip install uvicorn
pip install sqlmodel
pip freeze > requirements.txt
vscode 选择 venv 虚拟机
打开命令面板(Ctrl+Shift+P / Cmd+Shift+P); 输入「Python: Select Interpreter」(选择解释器); 选择后虚拟机 corepy 中的 python.exe
PostgreSQL 数据模型创建
与 FastAPI + SQLModel 适配性较好的数据库迁移工具主要有 Alembic(SQLAlchemy 官方配套)
安装 alembic
pip install alembic
pip install psycopg2-binary
初始化 Alembic
数据库迁移脚本放到 core/database/migrations 目录下
cd core
alembic init database/migrations
数据模型创建
这段代码是基于 SQLModel(FastAPI 生态的 ORM 工具)定义的用户数据模型,核心包含两个类:UserLogin 是用于接收前端登录请求的「数据校验模型」,UserModel 是映射数据库 user 表的「数据库模型」,既实现了请求参数校验,又完成了数据库表结构定义。
文件: core\modules\user\user_model.py
from datetime import datetime
from sqlalchemy import text
from sqlmodel import Field, SQLModel
class UserLogin(SQLModel):
username: str | None = Field(default=None, max_length=255, min_length=4)
password: str | None = Field(default=None, max_length=255, min_length=6)
class UserModel(UserLogin, table=True):
__tablename__ = "user" # Explicitly specify database table name
id: int | None = Field(default=None, primary_key=True)
create_at: datetime | None = Field(
default_factory=datetime.now,
# text() 会将其渲染为 DEFAULT CURRENT_TIMESTAMP(无引号,作为 PostgreSQL 函数执
sa_column_kwargs={"server_default": text("CURRENT_TIMESTAMP")}
)
配置 env.py 连接数据库
创建 target_metadata 用来创建数据库升级脚本, 所有模型都基于同一个 SQLModel.metadata,因此直接导出即可
文件: core/models/init.py
# 1. 统一导入 SQLModel(避免重复导入)
from sqlmodel import SQLModel
# 2. 导入所有分散的模型(按模块归类)
# 注意:只需导入模型类,无需导入其他内容
from user.user_model import UserModel
from reservation.reservation_model import ReservationModel
# 3. 导出 metadata(供 Alembic 使用)
# 所有模型都基于同一个 SQLModel.metadata,因此直接导出即可
target_metadata = SQLModel.metadata
修改 env.py, 将之前的 target_metadata 替换成你的版本:
from database import target_metadata
from database.db import SQLALCHEMY_DATABASE_URI
def get_url():
return str(SQLALCHEMY_DATABASE_URI)
# 修改创建脚本, 如
url = get_url()
connectable = create_engine(get_url())
创建数据库表和迁移指令
alembic revision --autogenerate -m "Initial migration"
若有错误:
# 1. 先备份数据库(重要!)
pg_dump -U postgres -d postgres > backup.sql # PostgreSQL 备份
# 2. 重置数据库迁移版本(清空 alembic_version 表)
alembic downgrade base # 回滚所有迁移(会删除表,谨慎!)
# 3. 重新生成初始脚本
alembic revision --autogenerate -m "Initial migration"
# 4. 应用新脚本
alembic upgrade head
应用数据库迁移
这个指令会将之前定义的 UserModel 和 ReservationModel 模型对应的表自动升级到PostgreSQL数据库
alembic upgrade head
回滚数据库(撤销迁移)
如果创建有误,可以撤消迁移
alembic downgrade -1
FastAPI 后端
入口文件
这段代码是 FastAPI 项目的核心入口文件,作用是初始化 FastAPI 应用、注册静态文件中间件、挂载业务接口路由,同时预留了另一种静态文件托管的方案
文件: core\main.py
from fastapi import FastAPI
from middleware.static_file import StaticFile
from modules.reservation.reservation_controller import router as reservation_router
from modules.user.user_controller import router as user_router
app = FastAPI()
app.add_middleware(StaticFile)
app.include_router(reservation_router)
app.include_router(user_router)
静态文件中间件
这段代码是 FastAPI 中一个静态文件中间件(StaticFile),核心作用是让 FastAPI 后端同时接管前端静态文件(如 React/Vue 打包后的 dist 目录)的访问,还能兼容前端路由的 “history 模式”(避免刷新页面 404)。
文件: core\middleware\static_file.py
from os import path
from fastapi import Response
from fastapi.responses import FileResponse
from starlette.middleware.base import BaseHTTPMiddleware
import os
import mimetypes
static_dir = path.join(os.getcwd(), "../web/dist")
class StaticFile(BaseHTTPMiddleware):
async def dispatch(self, request, call_next):
if request.url.path.startswith("/api/"):
return await call_next(request)
### path.join 跟 node.js 行为不一样, python要去除 /
file_path = path.join(static_dir, request.url.path.lstrip('/'))
if path.isdir(file_path):
file_path = path.join(file_path, "index.html")
# 目录不存在,则视为前端路由
if path.exists(file_path):
if path.isfile(file_path):
content_type, _ = mimetypes.guess_type(file_path) or "application/octet-stream"
return FileResponse(file_path, media_type = content_type)
else:
return FileResponse(path.join(static_dir, "index.html"), media_type="text/html; charset=utf-8")
return Response("Not Found", status_code=404)
创建路由Controller
这段代码是 FastAPI 中用户模块的接口路由控制器,核心作用是定义用户相关的 API 接口(登录、注册、查询列表),并将接口逻辑解耦到user_service层,同时用UserLogin/UserModel做请求参数校验,是典型的 “控制器 - 服务层” 分层设计。
文件: core\modules\user\user_controller.py
from fastapi import APIRouter
from modules.user.user_service import user_list, user_signin, user_signup
from modules.user.user_model import UserLogin, UserModel
router = APIRouter(
prefix="/api/v1/user",
tags=["User Module"],
responses={ 404: {"description": "User module not exist"} }
)
@router.post("/signin")
async def signin(userLogin: UserLogin):
user = await user_signin(userLogin)
return { "error": "" if user is not None else "User not found" }
@router.post('/signup')
async def signup(userInfo: UserModel):
exception = await user_signup(userInfo)
return { "error": "" if exception is None else str(exception) }
@router.get('/list')
async def list():
try:
users = await user_list()
return { "result": users }
except Exception as ex:
return { "error": str(ex) }
创建业务服务层Service
这段代码是 FastAPI 项目中用户模块的业务服务层(user_service),核心作用是实现用户注册、登录、查询列表的核心业务逻辑,基于SQLModel操作 PostgreSQL 异步数据库,
文件: core\modules\user\user_service.py
from sqlmodel import select
from database.db import get_async_session
from modules.user.user_model import UserLogin, UserModel
async def user_signup(data: UserModel):
async with get_async_session() as session:
# add是同步方法,不需要await!
session.add(data)
async def user_signin(userLogin: UserLogin):
async with get_async_session() as session:
statement = select(UserModel).where(
UserModel.username == userLogin.username and UserModel.password == userLogin.password
)
users = await session.exec(statement)
return users.first()
async def user_list():
async with get_async_session() as session:
statement = select(UserModel).order_by(UserModel.create_at)
users = await session.exec(statement)
return users.all()
启动后端程序
FastAPI 项目的启动命令(Windows 系统下),核心作用是先激活 Python 虚拟环境,再启动 FastAPI 的开发服务器。 用uvicorn(FastAPI 的官方服务器)启动项目,监听所有网络地址的 8000 端口,开启热重载
corepy\Scripts\activate
uvicorn main:app --host 0.0.0.0 --port 8000 --reload
React 前端
使用 vite 创建 react 项目:
npm create vite@latest web
运行这个项目
npm run:dev
项目结构
Project tree
package # 项目根目录
├── .gitignore # Git忽略文件配置
├── BUILD.md # 项目构建说明文档
├── core # 后端核心目录
│ ├── alembic.ini # Alembic配置文件
│ ├── database # 数据库相关目录
│ │ ├── db.py # 数据库连接配置
│ │ ├── migrations # Alembic迁移脚本目录
│ │ │ ├── env.py # Alembic核心配置
│ │ │ ├── README # 迁移目录说明
│ │ │ ├── script.py.mako # 迁移脚本模板
│ │ │ └── versions # 迁移脚本版本目录
│ │ └── __init__.py # 数据库模块初始化文件
│ ├── main.py # FastAPI应用入口文件
│ ├── middleware # 中间件目录
│ │ └── static_file.py # 静态文件中间件
│ ├── modules # 业务模块目录
│ │ ├── reservation # 预约模块
│ │ │ ├── reservation_controller.py # 预约接口控制器
│ │ │ ├── reservation_model.py # 预约数据模型
│ │ │ └── reservation_service.py # 预约业务逻辑
│ │ └── user # 用户模块
│ │ ├── user_controller.py # 用户接口控制器
│ │ ├── user_model.py # 用户数据模型
│ │ └── user_service.py # 用户业务逻辑
│ ├── requirements.txt # 后端依赖包清单
│ └── __init__.py # 核心模块初始化文件
├── README.md # 项目说明文档
├── SPEC.md # 项目规格说明
└── web # 前端目录
├── .gitignore # 前端Git忽略文件
├── .pnp.cjs # Yarn PnP配置文件
├── .pnp.loader.mjs # Yarn PnP加载器
├── eslint.config.js # ESLint代码检查配置
├── index.html # 前端入口HTML
├── package-lock.json # npm依赖锁文件
├── package.json # 前端项目配置(依赖、脚本)
├── public # 静态资源目录
│ └── vite.svg # 示例静态资源
├── README.md # 前端说明文档
├── src # 前端源码目录
│ ├── App.css # 根组件样式
│ ├── App.test.ts # 根组件测试文件
│ ├── App.tsx # 根组件
│ ├── assets # 资源文件目录
│ │ └── react.svg # React图标
│ ├── common # 通用工具目录
│ │ ├── ApiRequest.ts # API请求封装
│ │ └── ApiResponse.ts # API响应封装
│ ├── components # 通用组件目录
│ │ ├── DataList # 数据列表组件
│ │ ├── ReservationForm # 预约表单组件
│ │ └── SiteNav # 站点导航组件
│ ├── index.css # 全局样式
│ ├── main.tsx # 前端入口文件
│ ├── models # 前端数据模型
│ │ ├── Reservation.ts # 预约模型
│ │ └── User.ts # 用户模型
│ ├── pages # 页面组件目录
│ │ ├── Manage # 管理页面
│ │ ├── Sign # 登录/注册页面
│ │ └── Welcome # 欢迎页面
│ └── stores # 状态管理目录
│ └── UserStore.ts # 用户状态管理
├── tsconfig.app.json # TypeScript应用配置
├── tsconfig.json # TypeScript根配置
├── tsconfig.node.json # TypeScript Node配置
└── vite.config.ts # Vite构建配置
