Как обновить запрос, который был аннотирован?
У меня есть следующие модели:
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(..)
И это означает, что вы только отбиваете один запрос, а не один в другой, , но не используйте его, если ваш запрос зависит от аннотации для фильтрации. Это отлично подходит для удаления базы данных -генерированные конкатенации и утилитарный мусор, который я иногда добавляю в запросы модели по умолчанию... но пример в вопросе - прекрасный пример того, где это не сработает.
Ответ 3
Я продублировал эту проблему и считаю ее ошибкой с ORM Django. Ответ @acjay - хороший способ обхода проблемы. Отчет об ошибке: https://code.djangoproject.com/ticket/25171
Исправлено в Django 2 alpha: https://code.djangoproject.com/ticket/19513
Ответ 4
Чтобы добавить к ответу Оли: если вам нужны ваши аннотации к обновлению, сначала выполните фильтры и сохраните результат в переменной, а затем вызовите фильтр без аргументов в этом запросе для доступа к функции update
, например:
q = X.objects.filter(annotated_val=5, annotated_name='Nima')
q.query.annotations.clear()
q.filter().update(field=900)