Как обновить запрос, который был аннотирован?

У меня есть следующие модели:

class Work(models.Model):
    visible = models.BooleanField(default=False)

class Book(models.Model):
    work = models.ForeignKey('Work')    

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

qs=Work.objects.all()
qs.annotate(Count('book')).filter(Q(book__count__gt=1)).update(visible=False)

Однако это дает ошибку:

DatabaseError: в подзапросе слишком много столбцов LINE 1:... SET "visible" = false WHERE "app_work". "Id" IN (SELECT...

Если я удаляю предложение update, запрос запускается без проблем и возвращает то, что я ожидаю.

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

Ответ 1

Не создавая игрушечную базу данных, чтобы иметь возможность дублировать вашу проблему и попробовать решения, я могу хотя бы предложить подход в Django: получение дополнения к набору запросов как один возможный подход.

Попробуйте этот подход:

qs.annotate(Count('book')).filter(Q(book__count__gt=1))
Work.objects.filter(pk__in=qs.values_list('pk', flat=True)).update(visible=False)

Ответ 2

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

qs.query.annotations.clear()
qs.update(..)

И это означает, что вы только отбиваете один запрос, а не один в другой, , но не используйте его, если ваш запрос зависит от аннотации для фильтрации. Это отлично подходит для удаления базы данных -генерированные конкатенации и утилитарный мусор, который я иногда добавляю в запросы модели по умолчанию... но пример в вопросе - прекрасный пример того, где это не сработает.

Ответ 4

Чтобы добавить к ответу Оли: если вам нужны ваши аннотации к обновлению, сначала выполните фильтры и сохраните результат в переменной, а затем вызовите фильтр без аргументов в этом запросе для доступа к функции update, например:

q = X.objects.filter(annotated_val=5, annotated_name='Nima')
q.query.annotations.clear()
q.filter().update(field=900)