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

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

相关推荐
BaiPeter22 分钟前
ZPL文件预览
开发语言·c++
曲径海盗33 分钟前
【Python 学习第 53 天】列表基础
python·学习笔记·编程入门
Dumbness1 小时前
群里 @ 机器人发"查订单",指令一条都没命中:微信群机器人在路由前漏掉的几步归一化
python·微信
用户1614840955451 小时前
用 M4 Pro 跑羽毛球视频 AI 分析(二):TrackNet MPS 加速 3.2 倍、卡尔曼滤波补漏与自动回合剪辑
python
用户4019925568611 小时前
基于对话树回溯的社媒评论流买方意图识别与噪音过滤实践
python
用户0332126663672 小时前
使用 Python 加密或解密 PDF 文档
python
小七-七牛开发者2 小时前
让 Go 走得更远:用 LLGo 打通 Go 与 C/C++/Python 生态
python·编程·编译器·llvm·llgo
CyrusYiVPP3 小时前
实测 Ollama 本地部署:我用 Python 搓了个离线问答机器人,Qwen4 27B 一到换个模型名就能用
人工智能·python
2601_967980114 小时前
Python中flatten( )函数及函数用法详解
python·numpy·数组操作·flatten函数·列表处理
xbzb4 小时前
自定义 yum 软件源:Linux 上 createrepo,Windows 上纯 Python 也能造
linux·运维·windows·python·yum·createrepo