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

Django:更新对象浏览量时不修改updated_at字段的实现与疑问

问题背景与疑问

定义的模型

class Announcement(models.Model):
    ...
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    views = models.PositiveIntegerField(default=0, editable=False)

初始视图代码

class AnnouncementDetailView(DetailView):
    model = Announcement
    context_object_name = 'announcement'
    template_name = 'web/index.html'

    def get(self, *args, **kwargs):
        announcement = get_object_or_404(Announcement, id=kwargs['id'])
        announcement.views += 1
        announcement.save()
        return super().get(self, *args, **kwargs)

遇到的问题

  • 想知道除了F表达式外,更新字段但不修改updated_at的其他方法
  • 仅希望保存views字段变更,但调用save()会自动更新updated_at,导致页面刷新时该字段始终显示当前时间,不符合需求

修改后的视图代码

...
def get(self, *args, **kwargs):
    Announcement.objects.filter(id=kwargs['id']).update(views=F('views') + 1)
    return super().get(self, *args, **kwargs)

疑问

  1. 这种方式未调用Django模型的save()方法,为何能保存views字段的新值?
  2. 该方式的适用范围是什么?
  3. 不调用save()修改模型字段的最佳实践有哪些?

解答

1. 为何不调用save()也能保存字段值

Django的QuerySet.update()方法会直接生成SQL UPDATE语句发送给数据库执行,完全绕开了模型实例的save()方法流程。它不会触发模型的save()信号,也不会执行auto_now这类模型字段的自动逻辑,直接在数据库层面完成字段更新,所以能直接保存views的新值,同时不会修改updated_at。

2. QuerySet.update()的适用范围

  • 批量更新多条记录:比如给符合条件的所有公告增加浏览量,用filter(...).update()比循环实例调用save()效率高得多
  • 只需要更新单个/少数字段,且不需要触发模型save()相关的逻辑(比如信号、字段验证、auto_now更新等)
  • 避免并发更新问题:用F()表达式配合update()时,数据库层面会原子性地完成字段自增,不会出现多请求同时更新导致的计数丢失问题(你之前的announcement.views +=1再save()就有并发风险)

3. 不调用save()修改模型字段的最佳实践

  • 优先用QuerySet.update()配合F()表达式处理计数、状态变更等简单字段更新:原子性强,效率高,还能避免触发不必要的模型逻辑
  • 如果需要更新单个实例的部分字段且不想触发auto_now,可以用save(update_fields=['字段名']):比如你最初的场景,也可以改成:
    announcement = get_object_or_404(Announcement, id=kwargs['id'])
    announcement.views += 1
    announcement.save(update_fields=['views'])
    
    这种方式只会更新指定的字段,auto_now的updated_at不会被修改,同时会触发模型的save()信号(如果有需要的话)
  • 避免在循环中逐个实例调用save():数据量大时性能极差,换成批量update()或者bulk_update()更高效
  • 使用bulk_update()处理批量实例更新:当需要更新多个实例的不同字段值时,用bulk_update(实例列表, ['字段1', '字段2']),同样是直接生成批量SQL,比循环save()高效

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 09:55:27