在 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')

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

相关推荐
qq_33776320198 分钟前
国际期货资管系统开发方案|内外盘交易平台源码授权与二次开发支持
java·c语言·开发语言·c++·c#
不定积分要+C_yyy18 分钟前
Java关键字final三种用法、核心特性与高频易错点
java·开发语言
xexpertS2 小时前
队列积压治理:如何避免异步系统出现难以恢复的消息积压
java·开发语言
benchmark_cc9 小时前
如何用 Python + QuantDash 快速构建高胜率“配对交易(Pairs Trading)”策略?
开发语言·人工智能·python·pandas·量化交易·quantdash
程序员无隅10 小时前
Coding Agent 为什么压缩上下文后还能继续工作?上下文模块设计拆解
java·开发语言·数据库
Python+9910 小时前
Java 枚举类(Enum)详解:从基础到高级应用
java·开发语言·python
二炮手亮子11 小时前
浅记java线程池
java·开发语言
zmzb010311 小时前
C++课后习题训练记录Day157
开发语言·c++
dunge202611 小时前
2026年7月最新ChatGPT Plus / Pro 与 Codex:当 AI Agent 最新5.6版本来袭,必须理解事务、幂等与补偿
开发语言·人工智能·python
爱吃牛肉的大老虎12 小时前
rust基础之环境搭建
java·开发语言·rust