Подтвердить что ты не робот

Какая разница между select_related и prefetch_related в Django ORM?

В Django Doc,

select_related() "следует" за связями внешнего ключа, выбирая дополнительные данные связанного объекта при выполнении своего запроса.

prefetch_related() выполняет отдельный поиск для каждого отношения и выполняет "соединение" в Python.

Что это значит под "объединением в python"? Может кто-нибудь проиллюстрировать примером?

select_related я понимаю, для связи с внешним ключом используйте select_related; и для отношения M2M используйте prefetch_related. Это правильно?

4b9b3361

Ответ 1

Ваше понимание в основном верно. Вы используете select_related когда объект, который вы собираетесь выбрать, является отдельным объектом, то есть OneToOneField или ForeignKey. Вы используете prefetch_related когда собираетесь получить "набор" вещей, так что ManyToManyField как вы заявили, или обратный просмотр ForeignKey. Просто чтобы уточнить, что я подразумеваю под "обратным ForeignKey s", вот пример:

class ModelA(models.Model):
    pass

class ModelB(models.Model):
    a = ForeignKey(ModelA)

ModelB.objects.select_related('a').all() # Forward ForeignKey relationship
ModelA.objects.prefetch_related('modelb_set').all() # Reverse ForeignKey relationship

Разница в том, что select_related выполняет SQL-соединение и поэтому возвращает результаты как часть таблицы с SQL-сервера. prefetch_related с другой стороны, выполняет другой запрос и, следовательно, уменьшает избыточные столбцы в исходном объекте (ModelA в приведенном выше примере). Вы можете использовать prefetch_related для всего, для чего вы можете использовать select_related.

Компромисс заключается в том, что prefetch_related должен создать и отправить список идентификаторов для выбора обратно на сервер, это может занять некоторое время. Я не уверен, есть ли хороший способ сделать это в транзакции, но я понимаю, что Django всегда просто отправляет список и говорит SELECT... WHERE pk IN (...,...,...) в принципе. В этом случае, если предварительно выбранные данные редки (скажем, объекты государства США, связанные с адресами людей), это может быть очень хорошо, однако, если они ближе к однозначному, это может привести к потере большого количества сообщений. Если есть сомнения, попробуйте оба варианта и посмотрите, какие из них лучше.

Все, что обсуждалось выше, в основном касается связи с базой данных. Однако на стороне Python prefetch_related имеет дополнительное преимущество, prefetch_related том, что один объект используется для представления каждого объекта в базе данных. С помощью select_related дубликаты объектов будут создаваться в Python для каждого "родительского" объекта. Так как объекты в Python имеют приличную долю памяти, это также может быть рассмотрено.

Ответ 2

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

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

select_related выполняет объединение с каждым поиском, но расширяет выбор, чтобы включить столбцы всех связанных таблиц. Однако этот подход имеет оговорку.

Соединения имеют потенциал для умножения числа строк в запросе. Когда вы выполняете объединение по внешнему ключу или по отдельности, количество строк не увеличивается. Тем не менее, многие-ко-многим соединениям не имеют этой гарантии. Таким образом, Django ограничивает select_related отношениями, которые неожиданно не приведут к массивному соединению.

"join in python" для prefetch_related немного тревожит, тогда это должно быть. Он создает отдельный запрос для каждой связанной таблицы. Он фильтрует каждую из этих таблиц предложением WHERE IN, например:

SELECT "credential"."id",
       "credential"."uuid",
       "credential"."identity_id"
FROM   "credential"
WHERE  "credential"."identity_id" IN
    (84706, 48746, 871441, 84713, 76492, 84621, 51472);

Вместо того, чтобы выполнять одно соединение с потенциально слишком большим количеством строк, каждая таблица разделяется на отдельный запрос.

Ответ 3

prefetch_related используется для данных предварительной выборки для данных отношения "многие ко многим" и "многие к одному". select_related предназначен для выбора данных из одного значения отношения. Оба они используются для извлечения данных из их отношений из модели. Например, вы строите модель и модель, которая связана с другими моделями. Когда приходит запрос, вы также запрашиваете данные об их отношениях, и у Django есть очень хорошие механизмы для доступа к данным из их отношений, например, book.author.name, но когда вы повторяете список моделей для извлечения данных об их отношениях, Django создает каждый запрос для каждой отдельной информации об отношениях., Для преодоления этого у нас есть prefetchd_related и selected_related