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
相关产品推荐
相关产品推荐

