Django: невозможно обновить запрос после того, как был сделан фрагмент

Я пытаюсь сделать это:

UserLog.objects.filter(user=user).filter(action='message').filter(timestamp__lt=now)[0:5].update(read=True)

но я получаю эту ошибку:

Cannot update a query once a slice has been taken.

(используя django 1.2.1)

Что я делаю неправильно?

Ответы

Ответ 1

Как говорится в ошибке, вы не можете вызвать update() в QuerySet, если вы извлекли фрагмент.

Причина:

  • Взятие среза эквивалентно оператору LIMIT в SQL.
  • Выдача обновления превращает ваш запрос в оператор UPDATE.

То, что вы пытаетесь сделать, будет эквивалентно

UPDATE ... WHERE ... LIMIT 5

что невозможно, по крайней мере, не со стандартным SQL.

Ответ 2

В документации можно сделать что-то вроде следующего: я не уверен, выполняю ли ограничение во внутренних обходах QuerySet проверка вокруг вызова update() после нарезки:

inner_q = UserLog.objects.filter(user=user,
                                 action='message',
                                 timestamp__lt=now).values('pk')[0:5]
UserLog.objects.filter(pk__in=inner_q).update(read=True)

В противном случае вы можете использовать поиск in следующим образом:

ids = UserLog.objects.filter(user=user,
                             action='message',
                             timestamp__lt=now).values_list('pk', flat=True)[0:5]
UserLog.objects.filter(pk__in=list(ids)).update(read=True)

Ответ 3

Я получал ту же ошибку при попытке ограничить количество записей, возвращаемых набором запросов.

Я обнаружил, что если мы используем один из Django общие представления на основе классов, такие как ArchiveIndexView, мы можем использовать атрибут paginate_by =, чтобы ограничить количество записей.

Например (в views.py):

from django.views.generic import ArchiveIndexView
from .models import Entry

class HomeListView(ArchiveIndexView):
    """ Blog Homepage """
    model = Entry
    date_field = 'pub_date' 
    template_name = 'appname/home.html'
    queryset = Entry.objects.filter(
        is_active=True).order_by('-pub_date', 'title')
    paginate_by = 30

Ответ 5

Если вы хотите отрезать некоторые результаты запроса, вы можете скопировать его это к другой переменной (достаточно мелкой копии, которая быстрее чем глубокая копия, потому что она просто использует ссылки на оригинал объекты.)

import copy

queryset = Mytable.objects.all()
pieceOfQuery = copy.copy(queryset)
pieceOfQuery = pieceOfQuery[:10]

Это заставит Django жаловаться, если у вас есть фильтр order_by. ваш стол, так как это происходит после нарезки, если вы делаете это на главном объект запроса