#python #django #django-orm
#python #django #django-orm
Вопрос:
У меня есть список идентификаторов объектов, которые я получаю из запроса в методе модели, затем я использую этот список для удаления объектов из другой модели:
class SomeObject(models.Model):
# [...]
def do_stuff(self, some_param):
# [...]
ids_to_delete = {item.id for item in self.items.all()}
other_object = OtherObject.objects.get_or_create(some_param=some_param)
other_object.items.filter(item_id__in=ids_to_delete).delete()
Что мне не нравится, так это то, что для этого требуется 2 запроса (ну, технически 3 для get_or_create()
, но в реальном коде это на самом деле .filter(some_param=some_param).first()
вместо .get()
, так что я не думаю, что есть какой-то простой способ обойти это).
Как мне передать недооцененный набор запросов в качестве аргумента для __in
поиска?
Я хотел бы сделать что-то вроде:
ids_to_delete = self.items.all().values("id")
other_object.items.filter(item_id__in=ids_to_delete).delete()
Комментарии:
1. Можете ли вы поделиться моделью
OtherObject
иitems
?2. @WillemVanOnsem Я не думаю, что это поможет вам ответить на мой вопрос, это общий вопрос о том, как я могу использовать не оцененный набор запросов для
__in
3.ну, здесь
.delete()
будет удаленItem
s, а не его отношение к этим элементам (so.remove()
). Возможно, это не тот эффект, на который рассчитывали, поскольку вы могли бы сделать это,self.items.all().delete()
например, с помощью.
Ответ №1:
Вы можете передать a QuerySet
в запрос:
other_object.items.filter(id__in=self.items.all()).delete()
это преобразует его в подзапрос. Но не все базы данных, особенно MySQL, подходят для таких подзапросов. Кроме того, Django обрабатывает .delete()
вручную. Таким образом, он выполнит запрос для извлечения первичных ключей элементов, а затем запустит логику удаления (а также удалит элементы, которые имеют CASCADE
зависимость). So .delete()
выполняется не как один запрос, а как минимум два запроса, и часто большее количество из-за ForeignKey
s с on_delete
триггером.
Однако обратите внимание, что вы здесь удаляете Item
объекты, а не «отсоединяете» их от other_object
. Для этого можно использовать .remove(…)
[Django-doc].
Ответ №2:
Я должен был попробовать образец кода, который я опубликовал, вы действительно можете это сделать. Это приведено в качестве примера в документации, но в нем говорится: «будьте осторожны при использовании вложенных запросов и понимайте характеристики производительности вашего сервера базы данных» и рекомендует не делать этого, помещая подзапрос в список:
values = Blog.objects.filter(
name__contains='Cheddar').values_list('pk', flat=True)
entries = Entry.objects.filter(blog__in=list(values))
Комментарии:
1. Все зависит: приведение к списку иногда может значительно повысить общую производительность, даже если оно выполняет дополнительный запрос. Я видел, что запросы выполняются на три порядка медленнее, когда используется сложный подзапрос. Как правило, я оставляю как подзапрос, и если я замечаю проблемы с производительностью, я конвертирую в список (и комментирую, почему).