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

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

相关推荐
默_笙11 小时前
🍙 给每个请求过安检:FastAPI 是怎么把校验写进类型注解的
python
小羊没烦恼!11 小时前
初探性能优化——2个月到4小时的性能提升
java·开发语言·windows·算法·c#
qq_4260039611 小时前
启动playwright录制codegen生成自动化测试脚本
python·自动化
虎头金猫11 小时前
4K 视频总卡在公网带宽?用 N1 + OpenList 把网盘播放链路重新理顺
运维·服务器·网络·python·容器·beautifulsoup·pandas
长沙三为智能科技12 小时前
家政小程序开发从0到上线:五阶段交付流程与验收清单
python
伞伞悦读12 小时前
【第38期】Python 模块与包详解:import、from、模块搜索路径、包结构和 __init__
开发语言·python
只睡四小时13 小时前
Canvas 弹道联机实战:700 行 + 固定时间步长
python·websocket·html5·游戏开发·canvas
C语言小火车13 小时前
C/C++ 为什么需要编译器?
开发语言·c++
奇思妙想聪明勤奋的小羊13 小时前
DeepAgents第5章:子Agent 与上下文隔离—让 Agent学会委派
人工智能·python·学习·语言模型
lpfasd12314 小时前
2026年第38周GitHub趋势周报
python·科技·github