FastAPI中的复杂查询与原子更新指南
title: FastAPI中的复杂查询与原子更新指南
date: 2025/05/02 20:33:32
updated: 2025/05/02 20:33:32
author: cmdragon
excerpt:
FastAPI 结合 Tortoise-ORM 实现复杂查询与原子更新。通过 Q 对象构建多条件查询,支持 AND、OR、NOT 逻辑运算符,动态组合查询条件。使用 F 表达式进行原子更新,避免竞态条件,确保数据一致性。示例包括订单状态与金额的复杂查询、库存扣减的原子操作,以及商品促销的价格更新。常见错误包括字段拼写错误、类型不匹配和空结果集,需通过模型检查和异常处理解决。
categories:
- 后端开发
- FastAPI
tags:
- FastAPI
- Tortoise-ORM
- 复杂查询
- 原子更新
- Q对象
- F表达式
- 数据库操作

扫描二维码
关注或者微信搜一搜:编程智域 前端至全栈交流与成长
探索数千个预构建的 AI 应用,开启你的下一个伟大创意:https://tools.cmdragon.cn/
第一章:FastAPI复杂查询与原子更新实战
1. 环境准备与模型定义
在开始前确保已安装必要依赖:
pip install fastapi uvicorn tortoise-orm pydantic
创建订单模型示例(models.py):
from tortoise.models import Model
from tortoise import fields
class Product(Model):
id = fields.IntField(pk=True)
name = fields.CharField(max_length=255)
stock = fields.IntField(default=0)
price = fields.DecimalField(max_digits=10, decimal_places=2)
class Order(Model):
id = fields.IntField(pk=True)
status = fields.CharField(max_length=20) # pending/completed/canceled
total_amount = fields.DecimalField(max_digits=10, decimal_places=2)
product = fields.ForeignKeyField('models.Product', related_name='orders')
created_at = fields.DatetimeField(auto_now_add=True)
创建对应的Pydantic模型(schemas.py):
from pydantic import BaseModel
from datetime import datetime
class OrderOut(BaseModel):
id: int
status: str
total_amount: float
product_id: int
created_at: datetime
class Config:
orm_mode = True
2. 组合Q对象实现复杂查询
2.1 Q对象基础原理
Q对象是Tortoise-ORM的条件表达式构造器,支持逻辑运算符:
&表示AND|表示OR~表示NOT
示例:查询金额大于100且状态为pending的订单
from tortoise.expressions import Q
async def get_orders():
return await Order.filter(
Q(total_amount__gt=100) & Q(status="pending")
).all()
2.2 多条件动态组合
在路由中实现动态过滤(main.py):
from fastapi import APIRouter, Query
from tortoise.expressions import Q
router = APIRouter()
@router.get("/orders", response_model=list[OrderOut])
async def search_orders(
min_amount: float = Query(None),
max_amount: float = Query(None),
status: str = Query(None)
):
query = Q()
if min_amount:
query &= Q(total_amount__gte=min_amount)
if max_amount:
query &= Q(total_amount__lte=max_amount)
if status:
query &= Q(status=status)
return await Order.filter(query).prefetch_related('product')
2.3 复杂逻辑示例
查询过去7天内金额超过500的已完成订单,或金额低于100的待处理订单:
from datetime import datetime, timedelta
async def complex_query():
seven_days_ago = datetime.now() - timedelta(days=7)
return await Order.filter(
Q(
Q(created_at__gte=seven_days_ago) &
Q(total_amount__gt=500) &
Q(status='completed')
) |
Q(
Q(total_amount__lt=100) &
Q(status='pending')
)
).order_by('-created_at')
3. 使用F表达式进行原子更新
3.1 F表达式的作用原理
F表达式直接在数据库层面执行运算,避免竞态条件。示例:安全扣减库存
from tortoise.expressions import F
async def decrease_stock(product_id: int, quantity: int):
await Product.filter(id=product_id).update(
stock=F('stock') - quantity
)
3.2 复合更新操作
同时更新多个字段:
async def update_product_price(product_id: int, new_price: float):
await Product.filter(id=product_id).update(
price=new_price,
last_updated=datetime.now(),
version=F('version') + 1
)
3.3 条件更新示例
只有当库存充足时才允许扣减:
async def safe_purchase(product_id: int, quantity: int):
updated = await Product.filter(
id=product_id,
stock__gte=quantity
).update(stock=F('stock') - quantity)
if not updated:
raise HTTPException(400, "库存不足")
4. 完整案例演示
实现商品促销接口:
@router.post("/products/{product_id}/promotion")
async def create_promotion(
product_id: int,
discount: float = Body(..., gt=0, lt=1)
):
# 原子更新价格并记录操作
updated = await Product.filter(id=product_id).update(
price=F('price') * (1 - discount),
promotion_count=F('promotion_count') + 1
)
if not updated:
raise HTTPException(404, "商品不存在")
# 获取更新后的对象
product = await Product.get(id=product_id)
return {
"new_price": float(product.price),
"promotion_count": product.promotion_count
}
5. 常见报错解决方案
错误1:字段不存在
FieldError: Unknown field 'total_amout' for model Order
原因:字段名拼写错误(amout → amount)
解决:检查模型定义和查询字段是否一致
错误2:类型不匹配
ValidationError: 1 validation error for OrderOut...
原因:Decimal字段自动转换为float时精度丢失
解决:在Pydantic模型中使用Decimal类型并配置json_encoders
错误3:空结果集
DoesNotExist: Object does not exist
原因:查询条件过于严格导致无结果
解决:添加异常处理或使用first()代替get()
6. 课后Quiz
当需要同时满足三个条件时,Q对象应该如何组合?
A) Q(a) | Q(b) | Q(c)
B) Q(a) & Q(b) & Q(c)
C) Q(a) & (Q(b) | Q(c))
答案:B。& 运算符用于AND条件组合为什么要使用F表达式而不是先查询再更新?
A) 减少数据库查询次数
B) 避免并发导致的数据不一致
C) 两种方式效果相同
答案:B。F表达式保证原子操作,防止竞态条件如何防止通过Q对象构造的查询出现SQL注入?
A) 手动转义参数
B) 使用ORM的内置参数化查询
C) 限制用户输入字段
答案:B。Tortoise-ORM会自动处理查询参数化
余下文章内容请点击跳转至 个人博客页面 或者 扫码关注或者微信搜一搜:编程智域 前端至全栈交流与成长,阅读完整的文章:FastAPI中的复杂查询与原子更新指南 | cmdragon's Blog
往期文章归档:
- 深入解析Tortoise-ORM关系型字段与异步查询 | cmdragon's Blog
- FastAPI与Tortoise-ORM模型配置及aerich迁移工具 | cmdragon's Blog
- 异步IO与Tortoise-ORM的数据库 | cmdragon's Blog
- FastAPI数据库连接池配置与监控 | cmdragon's Blog
- 分布式事务在点赞功能中的实现 | cmdragon's Blog
- Tortoise-ORM级联查询与预加载性能优化 | cmdragon's Blog
- 使用Tortoise-ORM和FastAPI构建评论系统 | cmdragon's Blog
- 分层架构在博客评论功能中的应用与实现 | cmdragon's Blog
- 深入解析事务基础与原子操作原理 | cmdragon's Blog
- 掌握Tortoise-ORM高级异步查询技巧 | cmdragon's Blog
- FastAPI与Tortoise-ORM实现关系型数据库关联 | cmdragon's Blog
- Tortoise-ORM与FastAPI集成:异步模型定义与实践 | cmdragon's Blog
- 异步编程与Tortoise-ORM框架 | cmdragon's Blog
- FastAPI数据库集成与事务管理 | cmdragon's Blog
- FastAPI与SQLAlchemy数据库集成 | cmdragon's Blog
- FastAPI与SQLAlchemy数据库集成与CRUD操作 | cmdragon's Blog
- FastAPI与SQLAlchemy同步数据库集成 | cmdragon's Blog
- SQLAlchemy 核心概念与同步引擎配置详解 | cmdragon's Blog
- FastAPI依赖注入性能优化策略 | cmdragon's Blog
- FastAPI安全认证中的依赖组合 | cmdragon's Blog
- FastAPI依赖注入系统及调试技巧 | cmdragon's Blog
- FastAPI依赖覆盖与测试环境模拟 | cmdragon's Blog
- FastAPI中的依赖注入与数据库事务管理 | cmdragon's Blog
- FastAPI依赖注入实践:工厂模式与实例复用的优化策略 | cmdragon's Blog
- FastAPI依赖注入:链式调用与多级参数传递 | cmdragon's Blog
- FastAPI依赖注入:从基础概念到应用 | cmdragon's Blog
- FastAPI中实现动态条件必填字段的实践 | cmdragon's Blog
- FastAPI中Pydantic异步分布式唯一性校验 | cmdragon's Blog
- 掌握FastAPI与Pydantic的跨字段验证技巧 | cmdragon's Blog
- FastAPI中的Pydantic密码验证机制与实现 | cmdragon's Blog
- 深入掌握FastAPI与OpenAPI规范的高级适配技巧 | cmdragon's Blog
- Pydantic字段元数据指南:从基础到企业级文档增强 | cmdragon's Blog
- Pydantic Schema生成指南:自定义JSON Schema | cmdragon's Blog
- Pydantic递归模型深度校验36计:从无限嵌套到亿级数据的优化法则 | cmdragon's Blog
FastAPI中的复杂查询与原子更新指南的更多相关文章
- solr/solrj原子更新
lucene原子更新自己不用多介绍,但solr它的包装,下面是一个简单的介绍是:这个操作是用于索引非常有用. 详细在代码中使用例如以下: /** * 原子更新方式 * */ public static ...
- Solr搜索引擎【索引提交、事务日志、原子更新】
一.索引提交 当一个文档被添加到Solr中,但没有提交给索引之前,这个文档是无法被搜索的.换句话说,从查询的角度看,文档直到提交之后才是可见的.Solr有两种类型的提交:软提交和正常提交[也称硬提交] ...
- 利用带关联子查询Update语句更新数据
Update是T-sql中再简单不过的语句了,update table set column=expression [where condition],我们都会用到.但update的用法不仅于此,真 ...
- Oracle- 提示查询结果不可更新,请使用...更新结果。
我们在对Oracle数据库进行操作时,有时会在查询完结果后想要对其中的某些数据进行操作,当我们点击编辑(一个锁标志)是,会提示我们上述问题中的错误:这些查询结果不可更新,请使用ROWI或者SELECT ...
- 【转】PL/SQL编辑数据"这些查询结果不可更新,请包括ROWID或使用SELECT...FOR UPDATE获得可更新结果"处理
[转]PL/SQL编辑数据"这些查询结果不可更新,请包括ROWID或使用SELECT...FOR UPDATE获得可更新结果"处理 只要有人用了: select t.* from ...
- solr的原子更新/局部更新
solr支持三种类型的原子更新: set - to set a field. add - to add to a multi-valued field. inc - to increment a fi ...
- 【VB】操作ODBC-DAO方式操作只能查询,不能更新插入操作解决。
最近接手一个改善项目,需要从Access转化到SQL Server 2014,使用原有的ODBC连接方式只能查询,不能更新插入.网上一直找不到解决方案,然后自己测试一下使用ADO方式竟然可以连接了.具 ...
- mysql根据查询结果批量更新多条数据(插入或更新)
mysql根据查询结果批量更新多条数据(插入或更新) 1.1 前言 mysql根据查询结果执行批量更新或插入时经常会遇到1093的错误问题.基本上批量插入或新增都会涉及到子查询,mysql是建议不要对 ...
- SQL-在Update中进行子查询和左联查询
以下总结源自后边的三个参考思索和测试而来: 我们有一张行政区划表,为了查询速度的优化,我们需要在这张表中,将每个乡镇的记录中写入其所属的省.市.县, 表如下: 当然,我们可以使用游标或在存储过程中使用 ...
- mysql 在update中实现子查询的方式
当使用mysql条件更新时--最先让人想到的写法 UPDATE buyer SET is_seller=1 WHERE uid IN (SELECT uid FROM seller) 此语句是错误的, ...
随机推荐
- Luogu P7077 CSP-S2020 函数调用 题解 [ 蓝 ] [ 拓扑排序 ] [ 动态规划 ] [ 数学 ]
函数调用:个人非常喜欢的一道拓扑题. 转化 这题一共有三种操作,不太好搞.而第一个函数看起来就比较可做,第三个函数显然就是让你拓扑转移,于是我们考虑第二个操作怎么处理. 当我们进行一个操作一后,假设当 ...
- 面试官最想听到的Vue和React区别
前言 欧阳最近找工作面试时总是被问到两个问题:Vue和React的区别和从编译原理的角度来聊聊Vue的template和React的jsx.面试官问这些问题一般是想了解你对这两个框架的理解,所以这是一 ...
- ATT&CK实战系列(一)
环境下载 下载靶场环境,并导入虚拟机分别是win2003.win7.winserver2008 配置网络 虚拟机--编辑--虚拟机网络编辑器--添加网络VMnet2--仅主机模式分配的地址是192.1 ...
- MDK Debug时No target connected,STM32 ST-LINK Utility连接不上单片机的解决办法“Can not connect to target!”
芯片下载程序成功,再次下载时出现,以下错误. 点击确认后,如下提示. 或提示如下. 不管怎么设置都侦测不到芯片. 使用STM32 ST-LINK Utility连接单片机时提示下边错误 "C ...
- Docker Hub 无法访问,替代镜像
我使用以下配置成功拉取了mysql 8.0.33 和redis lastest,但是不知道究竟是哪一个起作用了 linux 执行 sudo vim /etc/docker/daemon.json 填入 ...
- 解决Typecho文章cid不连续的教程
Typecho下文章编号(cid)不连续,虽然不影响什么,也无关紧要,但是对于有强迫症的人(比如我)来说,真的是无法忍受.还好有大佬提供了解决办法. 将以下代码保存为php文件,上传至网站根目录,在浏 ...
- 大数据之路Week10_day05 (JavaAPI 操作Redis 第一阶段)
刚开始学习JavaAPI的时候,主要是对redis中的字符串,字节位图,列表,集合,有序集合进行操作,并能够完成简单的需求. package com.wyh.redis; import org.jun ...
- JS用 URL 构造函数来解析 URL
const url = new URL('http://username:password@hostname:9090/path?arg=value#anchor'); console.log(url ...
- 关于jsp的MySQL数据库连接问题
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding= ...
- 【vscode】vscode配置Java
[vscode]vscode配置Java 前言 配环境,需要记录,避免反复踩坑. 步骤 step1:官网走 配环境为什么不直接上官网教程,Visual Studio Code - Co ...