Python与数据库:SQLAlchemy实战指南
数据库操作是后端开发最核心的部分之一。在Python中直接写原生SQL虽然灵活但在项目变得复杂后ORM对象关系映射能帮我们节省大量时间。SQLAlchemy是Python生态中最强大的ORM框架这篇文章不讲太深的理论直接分享实际项目中最常用的操作和技巧。一、为什么选择SQLAlchemyPython的ORM有好几个选择但SQLAlchemy是公认的工业级解决方案。SQLAlchemy的核心优势支持多种数据库PostgreSQL、MySQL、SQLite、Oracle等提供了两种使用方式CoreSQL表达式和ORM对象映射性能优秀SQL生成高效连接池管理完善支持异步1.4版本对比其他ORM特性SQLAlchemyDjango ORMpeewee独立使用✅❌需要Django✅多数据库支持完整基本支持基本支持SQL生成能力极强中等中等学习曲线陡峭平缓平缓灵活性极高中等中等二、基础模型定义和连接配置首先安装bashpip install sqlalchemy # 根据使用的数据库安装对应驱动 pip install psycopg2-binary # PostgreSQL pip install pymysql # MySQL pip install aiomysql # MySQL异步驱动基础配置pythonfrom sqlalchemy import create_engine, Column, Integer, String, DateTime, Boolean, Text from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker, relationship from datetime import datetime # 创建数据库引擎 # 格式: 数据库类型://用户名:密码主机:端口/数据库名 engine create_engine( postgresql://user:passwordlocalhost:5432/mydb, echoTrue, # 打印生成的SQL调试时很有用 pool_size10, # 连接池大小 max_overflow20, # 连接池最大溢出数 pool_recycle3600, # 连接回收时间秒 pool_pre_pingTrue, # 使用前检测连接是否有效 ) # 创建基类 Base declarative_base() # 创建会话工厂 SessionLocal sessionmaker(autocommitFalse, autoflushFalse, bindengine)定义模型pythonfrom sqlalchemy import Column, Integer, String, DateTime, ForeignKey, Float, Text, Boolean from sqlalchemy.orm import relationship from datetime import datetime class User(Base): __tablename__ users id Column(Integer, primary_keyTrue, indexTrue) username Column(String(50), uniqueTrue, nullableFalse, indexTrue) email Column(String(100), uniqueTrue, nullableFalse) password_hash Column(String(200), nullableFalse) full_name Column(String(100)) age Column(Integer) is_active Column(Boolean, defaultTrue) created_at Column(DateTime, defaultdatetime.utcnow) updated_at Column(DateTime, defaultdatetime.utcnow, onupdatedatetime.utcnow) # 关系 orders relationship(Order, back_populatesuser, cascadeall, delete-orphan) def __repr__(self): return fUser(id{self.id}, username{self.username}) class Product(Base): __tablename__ products id Column(Integer, primary_keyTrue, indexTrue) name Column(String(200), nullableFalse) description Column(Text) price Column(Float, nullableFalse) stock Column(Integer, default0) category Column(String(50)) created_at Column(DateTime, defaultdatetime.utcnow) class Order(Base): __tablename__ orders id Column(Integer, primary_keyTrue, indexTrue) user_id Column(Integer, ForeignKey(users.id), nullableFalse) product_id Column(Integer, ForeignKey(products.id), nullableFalse) quantity Column(Integer, nullableFalse) total_amount Column(Float, nullableFalse) status Column(String(20), defaultpending) # pending, paid, shipped, completed, cancelled created_at Column(DateTime, defaultdatetime.utcnow) # 关系 user relationship(User, back_populatesorders) product relationship(Product) # 创建表 Base.metadata.create_all(engine)三、CRUD操作最常用的增删改查创建记录pythonfrom sqlalchemy.orm import Session def create_user(db: Session, username: str, email: str, password_hash: str): 创建用户 user User( usernameusername, emailemail, password_hashpassword_hash, created_atdatetime.utcnow() ) db.add(user) db.commit() db.refresh(user) # 刷新获取生成的id return user # 批量创建 def create_users_bulk(db: Session, users_data: list): 批量创建用户 users [User(**data) for data in users_data] db.add_all(users) db.commit() return users # 使用 with SessionLocal() as db: user create_user(db, zhangsan, zhangsanexample.com, hashed_password) print(user.id)查询记录pythonfrom sqlalchemy import and_, or_, not_, desc, func def get_user_by_id(db: Session, user_id: int): 根据ID获取用户 return db.query(User).filter(User.id user_id).first() def get_user_by_username(db: Session, username: str): 根据用户名获取用户 return db.query(User).filter(User.username username).first() def get_active_users(db: Session, skip: int 0, limit: int 100): 获取活跃用户列表 return db.query(User).filter(User.is_active True)\ .offset(skip).limit(limit).all() def search_users(db: Session, keyword: str): 搜索用户模糊匹配 return db.query(User).filter( or_( User.username.like(f%{keyword}%), User.email.like(f%{keyword}%), User.full_name.like(f%{keyword}%) ) ).all() def get_users_with_orders(db: Session): 获取有订单的用户使用join return db.query(User).join(Order).distinct().all() def get_user_statistics(db: Session): 获取用户统计信息聚合查询 total_users db.query(func.count(User.id)).scalar() active_users db.query(func.count(User.id)).filter(User.is_active True).scalar() avg_age db.query(func.avg(User.age)).scalar() return { total: total_users, active: active_users, avg_age: avg_age or 0 }更新记录pythondef update_user(db: Session, user_id: int, **kwargs): 更新用户信息 user db.query(User).filter(User.id user_id).first() if user: for key, value in kwargs.items(): if hasattr(user, key): setattr(user, key, value) user.updated_at datetime.utcnow() db.commit() db.refresh(user) return user def update_users_bulk(db: Session, user_ids: list, updates: dict): 批量更新用户 db.query(User).filter(User.id.in_(user_ids)).update( updates, synchronize_sessionFalse ) db.commit() # 使用 with SessionLocal() as db: # 单个更新 user update_user(db, 1, full_name张三丰, age30) # 批量更新 update_users_bulk(db, [1, 2, 3], {is_active: False})删除记录pythondef delete_user(db: Session, user_id: int): 删除用户软删除标记为不活跃 user db.query(User).filter(User.id user_id).first() if user: user.is_active False db.commit() return True return False def delete_user_permanent(db: Session, user_id: int): 永久删除用户 user db.query(User).filter(User.id user_id).first() if user: db.delete(user) db.commit() return True return False def delete_inactive_users(db: Session): 批量删除不活跃用户 deleted_count db.query(User).filter(User.is_active False).delete() db.commit() return deleted_count四、高级查询技巧复杂条件查询pythonfrom sqlalchemy import and_, or_, not_, between, in_ def complex_query(db: Session, filters: dict): 复杂条件查询 query db.query(User) # 组合条件 conditions [] if filters.get(min_age): conditions.append(User.age filters[min_age]) if filters.get(max_age): conditions.append(User.age filters[max_age]) if filters.get(username_like): conditions.append(User.username.like(f%{filters[username_like]}%)) if filters.get(active_only): conditions.append(User.is_active True) if filters.get(exclude_ids): conditions.append(not_(User.id.in_(filters[exclude_ids]))) if conditions: query query.filter(and_(*conditions)) return query.all()排序和分页pythonfrom sqlalchemy import desc, asc def paginated_query(db: Session, page: int 1, page_size: int 20, sort_by: str id, sort_order: str desc): 分页查询 # 计算偏移量 offset (page - 1) * page_size # 排序方向 order_func desc if sort_order desc else asc # 获取排序字段 sort_field getattr(User, sort_by, User.id) # 查询 query db.query(User).order_by(order_func(sort_field)) # 获取总数 total query.count() # 获取当前页数据 items query.offset(offset).limit(page_size).all() return { items: items, total: total, page: page, page_size: page_size, total_pages: (total page_size - 1) // page_size }子查询和复杂查询pythonfrom sqlalchemy import func, select def get_users_with_high_value_orders(db: Session, min_amount: float 1000): 获取有高额订单的用户子查询 subquery db.query(Order.user_id).filter(Order.total_amount min_amount).subquery() return db.query(User).filter(User.id.in_(subquery)).all() def get_order_statistics(db: Session): 订单统计分组查询 result db.query( User.username, func.count(Order.id).label(order_count), func.sum(Order.total_amount).label(total_amount), func.avg(Order.total_amount).label(avg_amount) ).join(Order, User.id Order.user_id)\ .group_by(User.id, User.username)\ .order_by(desc(total_amount))\ .all() return result五、事务管理pythonfrom sqlalchemy.exc import IntegrityError, SQLAlchemyError def transfer_order(db: Session, from_user_id: int, to_user_id: int, order_id: int): 转移订单事务示例 try: # 开始事务with块自动管理 with db.begin(): # 获取订单 order db.query(Order).filter(Order.id order_id).first() if not order: raise ValueError(订单不存在) # 更新订单所属用户 order.user_id to_user_id # 记录操作日志假设有日志表 # log OperationLog(...) # db.add(log) # 所有操作都成功才提交 # with块结束时会自动commit return True except IntegrityError as e: db.rollback() print(f数据完整性错误: {e}) return False except SQLAlchemyError as e: db.rollback() print(f数据库错误: {e}) return False # 更复杂的嵌套事务 def complex_transaction(db: Session): 复杂事务示例 try: # 方式1显式管理 db.begin_nested() # 保存点 try: # 执行一些操作 user db.query(User).filter(User.id 1).first() user.is_active False # 如果这里出错只会回滚到这个保存点 db.begin_nested() try: # 更细粒度的操作 order db.query(Order).filter(Order.id 1).first() order.status cancelled except Exception: db.rollback() # 回滚到第二个保存点 except Exception: db.rollback() # 回滚到第一个保存点 else: db.commit() except Exception as e: db.rollback() print(f事务失败: {e})六、性能优化技巧1. 使用selectinload避免N1查询pythonfrom sqlalchemy.orm import selectinload, joinedload # ❌ 错误方式N1查询 def get_orders_naive(db: Session): orders db.query(Order).all() for order in orders: # 每次访问order.user都会触发一次查询 print(order.user.username) # N次额外查询 # ✅ 正确方式预加载关联数据 def get_orders_optimized(db: Session): orders db.query(Order).options( selectinload(Order.user), # 一次查询加载所有用户 joinedload(Order.product) # 使用JOIN加载商品 ).all() for order in orders: print(order.user.username) # 不会触发额外查询2. 只查询需要的字段python# ❌ 查询所有字段 def get_all_users_fields(db: Session): return db.query(User).all() # ✅ 只查询需要的字段 def get_user_names_only(db: Session): return db.query(User.id, User.username, User.email).all() # 返回字典而不是对象 def get_user_dicts(db: Session): return db.query(User.id, User.username).all() # 返回命名元组列表3. 使用批量操作python# ❌ 逐条插入 def insert_users_one_by_one(db: Session, users_data: list): for data in users_data: user User(**data) db.add(user) db.commit() # ✅ 批量插入 def insert_users_bulk(db: Session, users_data: list): db.bulk_insert_mappings(User, users_data) db.commit()4. 合理使用索引pythonfrom sqlalchemy import Index # 在模型上定义索引 class User(Base): __tablename__ users id Column(Integer, primary_keyTrue) username Column(String(50)) email Column(String(100)) age Column(Integer) city Column(String(50)) # 复合索引 __table_args__ ( Index(idx_username_age, username, age), Index(idx_city_email, city, email), )七、异步SQLAlchemySQLAlchemy 1.4 支持异步操作配合FastAPI非常实用pythonfrom sqlalchemy.ext.asyncio import create_async_engine, AsyncSession from sqlalchemy.orm import sessionmaker from sqlalchemy import select # 创建异步引擎 async_engine create_async_engine( postgresqlasyncpg://user:passwordlocalhost:5432/mydb, echoTrue, pool_size10, ) # 异步会话工厂 AsyncSessionLocal sessionmaker( async_engine, class_AsyncSession, expire_on_commitFalse, ) # 异步CRUD async def get_user_async(user_id: int): async with AsyncSessionLocal() as db: result await db.execute( select(User).where(User.id user_id) ) return result.scalar_one_or_none() async def create_user_async(user_data: dict): async with AsyncSessionLocal() as db: user User(**user_data) db.add(user) await db.commit() await db.refresh(user) return user # 在FastAPI中使用 from fastapi import FastAPI app FastAPI() app.get(/users/{user_id}) async def get_user(user_id: int): user await get_user_async(user_id) if not user: return {error: User not found} return {id: user.id, username: user.username}八、常见问题与解决方案1. 连接池耗尽python# 增加连接池大小 engine create_engine( postgresql://..., pool_size20, # 基础连接数 max_overflow30, # 最大额外连接数 pool_pre_pingTrue, # 自动重连 )2. 数据库连接超时python# 设置连接超时和回收 engine create_engine( postgresql://..., connect_args{connect_timeout: 10}, pool_recycle3600, # 一小时后回收连接 )3. 查询性能慢使用explain()查看执行计划pythonfrom sqlalchemy import text def explain_query(db: Session): query db.query(User).filter(User.age 18) # 打印SQL print(str(query)) # 执行EXPLAIN result db.execute( text(fEXPLAIN ANALYZE {str(query)}) ) for row in result: print(row)总结SQLAlchemy功能强大但学习曲线确实陡峭。刚入门的朋友可以从核心功能开始逐步深入。学习建议先熟悉CRUD基础操作掌握查询过滤和排序学会处理关系映射了解性能优化技巧再研究高级特性记住SQLAlchemy的目标不是让你忘记SQL而是让SQL操作更安全、更高效。在复杂查询时有时候直接写原生SQL反而更清晰。

相关新闻

死亡细胞修改器下载 及其用法

死亡细胞修改器下载 及其用法

下载链接 转存 避免丢失 《死亡细胞》风灵月影修改器:技术解析与功能详解 《死亡细胞》(Dead Cells)作为一款将Roguelite与银河恶魔城机制融合的硬核动作游戏,以其流畅的战斗、严苛的死亡惩罚和丰富的武器构筑受到核心玩家喜爱。…

2026/10/12 18:00:04 阅读更多 →
左上角角标NEW、最新CSS代码

左上角角标NEW、最新CSS代码

demo <!DOCTYPE html> <html><head><style>/*角标签&#xff0c;父元素必须设置position: relative;overflow: hidden;height: 大于120;width: 大于120px;&#xff0c;同时&#xff0c;角标标签内加入属性superscriptTitle"左上角标签文字内容&q…

2026/10/4 1:29:17 阅读更多 →
嵌入式以太网MAC中断与地址过滤:寄存器级配置与实战避坑指南

嵌入式以太网MAC中断与地址过滤:寄存器级配置与实战避坑指南

1. 项目概述与核心价值 在嵌入式网络开发中&#xff0c;尤其是基于MCU的以太网应用&#xff0c;直接操作硬件寄存器往往是实现高性能、低延迟通信的必经之路。很多开发者习惯于依赖现成的驱动库或HAL&#xff08;硬件抽象层&#xff09;&#xff0c;这确实能快速上手&#xff0…

2026/10/9 11:02:04 阅读更多 →

最新新闻

用 Go 打造 sysinfo 工具:从 /proc 采集系统信息的实战指南

用 Go 打造 sysinfo 工具:从 /proc 采集系统信息的实战指南

简介&#xff1a;面向Go语言开发者与Linux运维人员的开源资源&#xff0c;聚焦系统信息采集场景。通过sysinfo库的封装&#xff0c;可便捷获取CPU使用率、内存状态、磁盘I/O、网络流量及文件系统容量等关键指标&#xff0c;适用于监控工具开发、性能分析、运维自动化脚本等任务…

2026/10/13 16:59:52 阅读更多 →
EPLAN Harness proD 2023:从图形驱动到数据驱动的线束设计范式革命

EPLAN Harness proD 2023:从图形驱动到数据驱动的线束设计范式革命

1. 这不是一次普通升级&#xff1a;EPLAN Harness proD 2023 的底层逻辑重写EPLAN Harness proD 2023 的发布&#xff0c;在电气线束工程圈子里没引发“哇哦”式的刷屏&#xff0c;反而让一批老用户在内部群聊里反复确认&#xff1a;“真不是补丁包&#xff1f;真重构了内核&am…

2026/10/13 16:59:52 阅读更多 →
Java聊天室开发实战:Socket多线程与群聊私聊完整实现

Java聊天室开发实战:Socket多线程与群聊私聊完整实现

简介&#xff1a;一款基于Java的实时聊天室项目&#xff0c;界面完整&#xff0c;提供群聊、私聊与在线用户列表功能&#xff0c;适合正在学习Java网络编程、多线程及GUI开发的初学者&#xff0c;也可作为课程设计与毕业设计的参考原型。压缩包内含42个文件&#xff0c;其中14个…

2026/10/13 16:59:52 阅读更多 →
哈特曼波前传感器标定:MATLAB仿真与响应矩阵构建指南

哈特曼波前传感器标定:MATLAB仿真与响应矩阵构建指南

简介&#xff1a;这是一份面向自适应光学与波前传感研究者的MATLAB仿真资源&#xff0c;围绕哈特曼波前传感器的标定与模式复原展开&#xff0c;核心解决Zernike多项式标定、Zernike模式复原过程以及DM&#xff08;变形镜&#xff09;响应函数广义逆求解等关键问题。资源共18个…

2026/10/13 16:59:52 阅读更多 →
数据可视化图表选择的底层逻辑与工程化决策方法

数据可视化图表选择的底层逻辑与工程化决策方法

1. 为什么90%的数据分析报告&#xff0c;图表选得都不对&#xff1f;我见过太多数据分析报告——页面排版精致&#xff0c;字体统一&#xff0c;配色考究&#xff0c;结论也写得头头是道。但只要把图表单独拎出来看&#xff0c;问题就暴露了&#xff1a;用堆叠柱状图展示时间趋…

2026/10/13 16:59:52 阅读更多 →
西瓜书4.5决策树实战:连续值与缺失值处理代码详解

西瓜书4.5决策树实战:连续值与缺失值处理代码详解

简介&#xff1a;这份资源是周志华《机器学习》&#xff08;西瓜书&#xff09;第4.5节决策树相关内容的配套代码包&#xff0c;面向正在学习机器学习基础算法、需要动手复现课本实验的初学者与进阶读者。包内共4个文件&#xff0c;以2个Jupyter Notebook为主&#xff0c;辅以1…

2026/10/13 16:58:51 阅读更多 →

日新闻

物联网系统集成避坑指南:从选型到验收的关键要点

物联网系统集成避坑指南:从选型到验收的关键要点

做了这么多年物联网系统集成的项目&#xff0c;我最大的感受是&#xff0c;大部分坑不是出在技术前沿&#xff0c;而是出在一些看起来很平常的地方。设备选了便宜的、协议没问清楚、现场没摸透、验收标准写得含糊——这些细枝末节&#xff0c;最后都会变成延期、超支和扯皮的大…

2026/10/13 0:00:57 阅读更多 →
ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

如果你玩ESP32有一阵子了&#xff0c;很难不碰到舵机控制这道坎。做智能小车要转方向&#xff0c;做机械臂要抬关节&#xff0c;做云台要偏航俯仰&#xff0c;背后都是同一个东西在干活&#xff1a;舵机。而我见过太多人卡在“代码能编译、舵机就是不听话”这个阶段&#xff0c…

2026/10/13 0:00:57 阅读更多 →
CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

1. 为什么CAN总线至今仍是工业现场的“硬通货”&#xff1f;——从汽车底盘到智能温室的真实需求你有没有拆过一辆老款燃油车的中控台&#xff1f;或者调试过某高校实验室里那台跑着Modbus协议却频频丢帧的PLC控制柜&#xff1f;又或者&#xff0c;在某个农业物联网项目里&…

2026/10/13 0:00:57 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介&#xff1a;基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码&#xff0c;面向计算机相关专业课程设计与期末大作业学生&#xff0c;以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程&#xff0c;…

2026/10/13 2:54:56 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程&#xff1a;键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化&#xff0c;十个新手有八个栽在"往输入框里填东西"这件事上&#xff1a;要么填不进去&#xff0c;要么填了一半&#xff0c;要么直接把原来内容追加在后面。这背后的根因&…

2026/10/13 16:22:04 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程&#xff1a;阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀&#xff1a;什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面&#xff0c;跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/13 1:58:08 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 21:05:09 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 14:57:39 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 18:11:17 阅读更多 →