Django: Увеличить количество просмотров записей в блоге на единицу. Это эффективно? - PullRequest
21 голосов
/ 15 января 2009

В моем представлении индекса есть следующий код.

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 достаточно «умен», чтобы выполнить только один вызов обновления?

Есть ли более эффективный метод для достижения этого результата?

Ответы [ 5 ]

52 голосов
/ 20 мая 2009

Для этого вы можете использовать F() объектов.

Вот как вы импортируете F: from django.db.models import F

Новое в Django 1.1 .
Призывы к обновлению также могут использовать объекты F () для обновления одного поля на основе значения другого поля в модели. Это особенно полезно для увеличения счетчиков на основе их текущего значения.

Entry.objects.filter(is_published=True).update(views=F('views')+1)

Несмотря на то, что вы не можете выполнить обновление для нарезанного набора запросов ... изменить: на самом деле вы можете ...

Это можно сделать полностью в django ORM. Вам нужно два SQL-запроса:

  1. Сделай свой фильтр и собери список первичных ключей
  2. Выполните обновление для нерезанного набора запросов, соответствующих любому из этих первичных ключей.

Трудно получить набор неразрезанных запросов. Я задавался вопросом об использовании 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 лениво выполняет запросы, это приводит к двум попаданиям в базу данных, независимо от того, сколько элементов обновлено.

13 голосов
/ 15 января 2009

Вы можете обрабатывать обновления в одной транзакции, что может значительно повысить производительность. Используйте отдельную функцию, оформленную с помощью @action.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 голосов
/ 15 января 2009

Если вам действительно нужна эффективность, на данный момент вам нужно перейти в SQL и запустить обновление самостоятельно. Это не стоит дополнительной сложности в этом случае, однако.

В Django 1.1 вы сможете сделать это за один вызов SQL через ORM, используя F () объекты для ссылки на поля в значении обновления.

3 голосов
/ 15 января 2009

Пересмотренный

Вы обновляете 10 отдельных отдельных объектов.

10 отдельных, отдельных, отличных обновлений не могут быть легко объединены в одно волшебное обновление, которое так или иначе касается 10 объектов.

2 голосов
/ 11 января 2010

Улучшение производительности по сравнению с предыдущей записью. Это приводит к одному обращению к базе данных с подзапросом.

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'
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...