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')
你可以根据自己的需要选择使用哪种解决方案。