在 Django 中高效更新博客文章浏览次数

1、问题背景

在 Django 中,我想更新博客文章的浏览次数,以便在文章列表中显示最新的浏览量。我使用以下代码在索引视图中实现此功能:

python 复制代码
latest_entry_list = Entry.objects.filter(is_published=True).order_by('-date_published')[:10]
for entry in latest_entry_list:
    entry.views = entry.views + 1
    entry.save()

我的问题是:如果从初始查询中返回了十行(限制),那么 save 会向数据库发出 10 个单独的更新调用,还是 Django 足够"智能",只发出一个更新调用?是否有更有效的方法来实现这个结果?

2、解决方案

有几种方法可以解决这个问题,下面是其中一些:

方法一:使用 F() 对象

从 Django 1.1 开始,可以使用 F() 对象在更新中引用字段。这对于基于当前值递增计数器特别有用。以下是如何使用 F() 对象来更新博客文章的浏览次数:

python 复制代码
from django.db.models import F

Entry.objects.filter(is_published=True).update(views=F('views') + 1)

这种方法可以将所有更新合并为一个数据库调用,从而提高性能。

方法二:使用事务

另一种提高性能的方法是使用事务来处理更新。事务可以确保所有更新都成功完成,或者全部失败。以下是如何使用事务来更新博客文章的浏览次数:

python 复制代码
@transaction.commit_manually
def update_latest_entries(latest_entry_list):
    for entry in latest_entry_list:
        entry.views += 1
        entry.save()
    transaction.commit()

这种方法也可以将所有更新合并为一个数据库调用,从而提高性能。

方法三:使用子查询

最后,还可以使用子查询来更新博客文章的浏览次数。子查询可以将多个查询组合成一个查询。以下是如何使用子查询来更新博客文章的浏览次数:

python 复制代码
latest_entry_query_set = Entry.objects.filter(is_published=True) \
                                      .order_by('-date_published')[:10]
non_sliced_query_set = Entry.objects.filter(pk__in=latest_entry_query_set.values('id'))
n = non_sliced_query_set.update(views=F('views') + 1)
print(n or 0, 'items updated')

这种方法也可以将所有更新合并为一个数据库调用,从而提高性能。

3、代码示例

以下是如何在你的 Django 项目中使用上述解决方案的示例代码:

python 复制代码
from django.db.models import F
from django.db import transaction

def update_latest_entries(latest_entry_list):
    # 使用 F() 对象更新浏览次数
    Entry.objects.filter(is_published=True).update(views=F('views') + 1)

    # 使用事务更新浏览次数
    @transaction.commit_manually
    def update_latest_entries(latest_entry_list):
        for entry in latest_entry_list:
            entry.views += 1
            entry.save()
        transaction.commit()

    # 使用子查询更新浏览次数
    latest_entry_query_set = Entry.objects.filter(is_published=True) \
                                          .order_by('-date_published')[:10]
    non_sliced_query_set = Entry.objects.filter(pk__in=latest_entry_query_set.values('id'))
    n = non_sliced_query_set.update(views=F('views') + 1)
    print(n or 0, 'items updated')

你可以根据自己的需要选择使用哪种解决方案。

相关推荐
用户7783366132115 小时前
用 React Hook 封装搜索数据:useSerp 的防抖、缓存与错误处理
python·api
65岁退休Coder9 小时前
LangGraph v1.2.9 节点容错策略 & 流式输出 & 持久化记忆管理
后端·python·langchain
ikun_文11 小时前
Django框架路由Router的使用
python·pycharm·django
IvanCodes11 小时前
Python 基础语法(二):字符串与常用操作
python
昭昭日月明11 小时前
LangChain 生态:从链到代理,开发者需要掌握的三大核心
python·langchain·agent
Csvn11 小时前
🐍 Day 8:面向对象编程
后端·python
程序员天天困11 小时前
向量检索不准怎么办:混合检索与 Rerank 重排序召回优化实战
后端·python·ai编程
alphaTao13 小时前
LeetCode 每日一题 2026/8/24-2026/8/30
python·算法·leetcode
苏灿烤鱼13 小时前
当 AI Agent 遇见真实科学环境:深度拆解 Scientific Agent Skills,把"聊天机器人"变成"AI 科学家"
python·开源·agent
张文君13 小时前
ubuntu26.04坏道坏块分区隔离急速版260831-V0.12
linux·python