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

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

相关推荐
方也_arkling20 小时前
【Java-Day08】static / final / 枚举
java·开发语言
风吹夏回20 小时前
Python 全局异常处理:从“满屏 try-except”到优雅兜底
开发语言·python
Chengbei1120 小时前
一站式源码安全检测工具、云安全 / APP / 小程序源码敏感信息递归多层目录扫描AK、JWT、手机号、身份证等敏感信息
java·开发语言·安全·web安全·网络安全·系统安全·安全架构
llz_11220 小时前
web-第一次课后作业
java·开发语言·idea
小熊Coding21 小时前
Python爬取当当网二手图书项目实战!
开发语言·爬虫·python·beautifulsoup·requests·二手图书
秋921 小时前
Java项目运行5天左右自动宕机:系统性定位与解决方案
java·开发语言·python
小江的记录本21 小时前
【JVM虚拟机】垃圾回收GC:垃圾收集器:CMS:核心原理、回收流程、优缺点、废弃原因(附《思维导图》+《面试高频考点清单》)
java·jvm·后端·python·spring·面试·maven
xiaoshuaishuai821 小时前
C# 内存管理与资源泄漏
开发语言·c#
lsx20240621 小时前
SVN 检出操作
开发语言
田里的水稻21 小时前
OE_ubuntu26.04与宿主机之间复制粘贴内容
人工智能·python·机器人