Передать набор запросов в качестве аргумента __in в django?

#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. Все зависит: приведение к списку иногда может значительно повысить общую производительность, даже если оно выполняет дополнительный запрос. Я видел, что запросы выполняются на три порядка медленнее, когда используется сложный подзапрос. Как правило, я оставляю как подзапрос, и если я замечаю проблемы с производительностью, я конвертирую в список (и комментирую, почему).