You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django按ID过滤更新模型字段后,如何避免重复查询?

问题:避免Django中更新用户后的重复数据库查询

需求与问题背景

需要编写处理GET请求的函数,将User模型的inaction字段设为2、lastAction字段设为datetime.now()。

最初的错误代码

运行时抛出TypeError: cannot unpack non-iterable int object错误:

def update(self, res_id: str):
        user, updated = User.objects.filter(id=res_id).update(inaction="2", lastAction=datetime.now())
        code_status = HTTPStatus.ACCEPTED if updated else HTTPStatus.OK.value
        return user, code_status

错误原因:Django的QuerySet.update()方法仅返回受影响的行数(整数),并非可解包的(user, updated)元组,因此无法进行解包操作。

可运行但存在重复查询的代码

修改后代码可正常执行,但执行了两次User.objects.filter(id=res_id)数据库查询:

def update(self, res_id: str):
        updated_rows = User.objects.filter(id=res_id).update(inaction="2", lastAction=datetime.now())
        user = User.objects.filter(id=res_id).first()
        code_status = HTTPStatus.ACCEPTED if updated_rows else HTTPStatus.OK.value
        return user, code_status

相关模型代码

models.py:

class User(models.Model):
    operatorId = models.CharField(max_length=64, null=False)
    createdAt = models.DateTimeField(auto_now_add=True, null=True)
    operator = models.CharField(max_length=10, null=True)
    inaction = models.IntegerField(default=1)
    lastAction = models.DateTimeField(null=True)

    class Meta:
        verbose_name = 'user'
        verbose_name_plural = 'users'
        db_table = "users"
        ordering = ('-createdAt',)

    def __str__(self) -> str:
        return UsersEntity.to_string(self)

entities.py:

class UsersEntity:
    id: int
    operatorId: str = None
    createdAt: date = None
    operator: str = None
    inaction: int = 1

    @staticmethod
    def to_string(user: 'UsersEntity') -> str:
        operatorId = f"({user.operatorId}):" if user.operatorId else ValueError
        return f'{user.id} -> {operatorId}'

解决方法:避免重复查询

以下两种方式可有效减少数据库查询次数:

方法1:先获取实例再更新(单条记录场景)

如果res_id对应单条用户记录,先通过get()或first()获取User实例,直接修改字段后调用save(),仅需一次查询:

from datetime import datetime

def update(self, res_id: str):
    try:
        user = User.objects.get(id=res_id)
        user.inaction = 2  # 模型中该字段为IntegerField,直接用整数而非字符串
        user.lastAction = datetime.now()
        user.save(update_fields=['inaction', 'lastAction'])  # 指定更新字段,降低数据库开销
        code_status = HTTPStatus.ACCEPTED
        return user, code_status
    except User.DoesNotExist:
        code_status = HTTPStatus.OK.value
        return None, code_status
  • 优势:直接操作实例,无需二次查询;update_fields参数仅更新修改的字段,提升性能。
  • 注意:若res_id可能对应多条记录,改用filter().first()替代get()避免抛出异常。

方法2:update() + select_for_update()(并发/批量场景)

若需处理并发修改或批量更新,可先查询并锁定记录,再执行更新,最后刷新实例获取最新值:

from datetime import datetime
from django.db import transaction

def update(self, res_id: str):
    with transaction.atomic():
        # 查询并锁定记录,避免并发修改导致脏数据
        user = User.objects.select_for_update().filter(id=res_id).first()
        if not user:
            return None, HTTPStatus.OK.value
        # 执行更新操作
        updated_rows = User.objects.filter(id=res_id).update(inaction=2, lastAction=datetime.now())
        # 刷新实例,获取数据库中最新字段值
        user.refresh_from_db()
        code_status = HTTPStatus.ACCEPTED if updated_rows else HTTPStatus.OK.value
        return user, code_status
  • 优势:适合高并发场景,保证操作原子性;仅执行两次数据库操作(查询锁+更新),比原代码的两次普通查询更安全。
  • 注意:必须配合事务使用,确保操作的原子性。

额外优化提示

  • 原代码中inaction="2"为字符串类型,与模型中IntegerField字段类型不匹配,应改为整数2,避免隐式类型转换。
  • 从RESTful设计规范来看,更新资源更适合使用PUT/PATCH请求,GET请求通常用于查询资源。

内容的提问来源于stack exchange,提问作者pipka2

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 15:33:41