直接用 model.field += 1 会引发竞态条件,因 Python 层先读、再算、再写,多请求并发时可能覆盖彼此结果;F() 将自增逻辑下推至数据库 SQL 层原子执行,如 MyModel.objects.filter(id=123).update(view_count=F('view_count') + 1)。

为什么直接用 model.field += 1 会出错?
因为 Python 层的 += 是先读、再算、再写,中间可能被其他请求覆盖。比如两个并发请求同时读到 count=5,各自加 1 后都写回 6,结果只 +1 而不是 +2。Django 的 F 对象把计算逻辑下推到数据库层,由 SQL 原子执行,避免竞态。
怎么用 F 实现字段自增?
核心是用 F 包裹字段名,再参与表达式。必须配合 .update() 或 .save(update_fields=...)(后者需额外处理):
-
MyModel.objects.filter(id=123).update(view_count=F('view_count') + 1)—— 最常用,无须实例化,纯 SQL 执行 - 如果已有一个实例且需触发模型方法或信号,改用:
obj = MyModel.objects.get(id=123); obj.view_count = F('view_count') + 1; obj.save(update_fields=['view_count']) - 注意:
F只在 ORM 查询/更新中生效,不能用于普通赋值或打印(print(F('view_count'))没有意义)
常见错误:F 和普通值混用导致 SQL 错误
比如 .update(score=F('score') + request.POST.get('delta')),若用户传入非数字字符串,会生成非法 SQL。Django 不做运行时类型校验,数据库直接报错 invalid input syntax for integer。
图片提示词生成器?不止如此。 马甲系统 —— 把脑海中的画面,翻译成AI能理解的专业表达。 用得越多,它越懂你:首次需要多问几句确认方向,用久了几乎一说就懂。 用得越多,它越快:缓存机制让后续对话越来越省。 RAG进化:成功案例持续入库,越跑越聪明。 输入「新手指南」查看完整功能介绍
- 务必先转换并验证:
delta = int(request.POST.get('delta', '0')) - 更安全的做法是用数据库函数限定范围:
F('score') + Value(delta, output_field=IntegerField()) - 负数自增也合法:
F('balance') - 100,但要注意字段是否允许负值
自增后还想拿到新值?F 本身不返回结果
F 是表达式构造器,不是计算器。调用 .update() 只返回影响行数(int),不会返回新字段值。
立即学习“Python免费学习笔记(深入)”;
- 如果必须立刻知道新值,得再查一次:
obj.refresh_from_db(fields=['view_count']) - PostgreSQL 用户可用
RETURNING,但 Django 原生不支持,需绕过 ORM:with connection.cursor() as c: c.execute("UPDATE ... RETURNING view_count", [...]) - MySQL 无
RETURNING,只能查两次——这是容易被忽略的性能代价

















