В Django Doc,
select_related()
"следует" за связями внешнего ключа, выбирая дополнительные данные связанного объекта при выполнении своего запроса.
prefetch_related()
выполняет отдельный поиск для каждого отношения и выполняет "соединение" в Python.
Что это значит под "объединением в python"? Может кто-нибудь проиллюстрировать примером?
select_related
я понимаю, для связи с внешним ключом используйте select_related
; и для отношения M2M используйте prefetch_related
. Это правильно?
Ваше понимание в основном верно. Вы используете 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 имеют приличную долю памяти, это также может быть рассмотрено.
select_related
- это один запрос, а prefetch_related
- два, поэтому первый - быстрее. Но select_related
не поможет вам для ManyToManyField
Оба метода достигают той же цели, чтобы отказаться от ненужных запросов 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);
Вместо того, чтобы выполнять одно соединение с потенциально слишком большим количеством строк, каждая таблица разделяется на отдельный запрос.