Django: увеличение количества записей в блоге на единицу. Является ли это эффективным?
В моем индексном представлении есть следующий код.
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()
Если есть десять (предельных) строк, возвращаемых из первоначального запроса, будет ли проблема сохранения 10 разделять обновленные вызовы в базе данных или Django "smart" достаточно, чтобы выпустить только один вызов обновления?
Есть ли более эффективный метод для достижения этого результата?
Ответы
Ответ 1
Вы можете использовать для этого объекты F()
.
Вот как вы импортируете F
: from django.db.models import F
Новое в Django 1.1.
Вызовы для обновления также могут использовать объекты F() для обновления одного поля на основе значения другого поля в модели. Это особенно полезно для увеличения счетчиков на основе их текущего значения.
Entry.objects.filter(is_published=True).update(views=F('views')+1)
Хотя вы не можете выполнить обновление в наборе заданных наборов... edit: на самом деле вы можете...
Это можно сделать полностью в ORM django. Вам нужны два SQL-запроса:
- Сделайте свой фильтр и соберите список первичных ключей
- Сделайте обновление для набора нестандартных запросов, соответствующих любому из этих первичных ключей.
Получение нерезанного набора запросов - это бит. Я задавался вопросом об использовании in_bulk
, но возвращает словарь, а не набор запросов. Обычно можно было бы использовать Q objects
для выполнения сложных запросов типа OR, и это будет работать, но pk__in
делает работу намного проще.
latest_entry_ids = Entry.objects.filter(is_published=True)\
.order_by('-date_published')
.values_list('id', flat=True)[:10]
non_sliced_query_set = Entry.objects.filter(pk__in=latest_entry_ids)
n = non_sliced_query_set.update(views=F('views')+1)
print n or 0, 'items updated'
Из-за того, что django выполняет запросы лениво, это приводит только к двум ударам базы данных, независимо от того, сколько элементов обновлено.
Ответ 2
Вы можете обрабатывать обновления в одной транзакции, что может значительно повысить производительность. Используйте отдельную функцию, украшенную @transaction.commit_manually.
@transaction.commit_manually
def update_latest_entries(latest_entry_list):
for entry in latest_entry_list:
entry.views += 1
entry.save()
transaction.commit()
Ответ 3
Исправленный вариант
Вы обновляете 10 отдельных, отдельных, отдельных объектов.
10 отдельных отдельных отдельных изменений не могут быть легко свернуты в одно волшебное обновление, которое каким-то образом затрагивает 10 объектов.
Ответ 4
Если вам действительно нужна эффективность, на данный момент вам придется спуститься в SQL и запустить обновление самостоятельно. Тем не менее, это не стоит добавленной сложности.
По Django 1.1 вы сможете сделать это в одном вызове SQL через ORM, используя F() objects для ссылок на поля в значении обновления.
Ответ 5
Улучшение производительности предыдущей записи. Это приводит к одному удалению базы данных с подзапросом.
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'