В Django Doc,
select_related()
"следует" отношениям внешнего ключа, выбирая дополнительные данные связанного объекта, когда он выполняет свой запрос.
prefetch_related()
выполняет отдельный поиск для каждого отношения и выполняет «соединение» в Python.
Что это значит под "объединением в python"? Может кто-нибудь проиллюстрировать примером?
Насколько я понимаю, для отношений с внешним ключом используйте select_related
; и для отношений M2M используйте prefetch_related
. Это верно?
python
django
django-models
django-orm
NeoWang
источник
источник
Ответы:
Ваше понимание в основном верно. Вы используете,
select_related
когда объект, который вы собираетесь выбрать, представляет собой один объект, так чтоOneToOneField
илиForeignKey
. Вы используете,prefetch_related
когда собираетесь получить «набор» вещей, так же,ManyToManyField
как вы заявили, или наоборотForeignKey
. Просто чтобы прояснить, что я имею в виду под «обратнымForeignKey
с», вот пример:Разница в том, что
select_related
выполняется соединение SQL и, следовательно, результаты возвращаются как часть таблицы с сервера SQL.prefetch_related
с другой стороны, выполняет другой запрос и, следовательно, уменьшает избыточные столбцы в исходном объекте (ModelA
в приведенном выше примере). Вы можете использоватьprefetch_related
для всего, что вы можете использоватьselect_related
для.Компромисс
prefetch_related
заключается в том, что необходимо создать и отправить список идентификаторов для выбора обратно на сервер, это может занять некоторое время. Я не уверен, есть ли хороший способ сделать это в транзакции, но я понимаю, что Django всегда просто отправляет список и говорит SELECT ... WHERE pk IN (..., ..., ...) в принципе. В этом случае, если предварительно выбранные данные редки (скажем, объекты Государства США, связанные с адресами людей), это может быть очень хорошо, однако, если они ближе к однозначному, это может привести к потере большого количества сообщений. Если есть сомнения, попробуйте оба варианта и посмотрите, какие из них работают лучше.Все, что обсуждалось выше, в основном касается связи с базой данных. Однако на стороне Python
prefetch_related
есть дополнительное преимущество в том, что один объект используется для представления каждого объекта в базе данных. При этомselect_related
дубликаты объектов будут создаваться в Python для каждого «родительского» объекта. Так как объекты в Python имеют приличную долю памяти, это также может быть рассмотрено.источник
select_related
это один запрос, аprefetch_related
два, так что первый быстрее. Ноselect_related
не поможет дляManyToManyField
«sselect_related
использует JOIN в SQL, тогда какprefetch_related
запускает запрос для первой модели, собирает все идентификаторы, необходимые для предварительной выборки, а затем запускает запрос с предложением IN в WHERE со всеми необходимыми идентификаторами. Если вы скажете 3-5 моделей, использующих один и тот же внешний ключ,select_related
почти наверняка будет лучше. Если у вас есть сотни или тысячи моделей, использующих один и тот же внешний ключ,prefetch_related
может быть лучше. В промежутке между тем вам придется проверить и посмотреть, что произойдет.Оба метода достигают одной и той же цели, чтобы избежать ненужных запросов к базе данных. Но они используют разные подходы для эффективности.
Единственная причина использования любого из этих методов - когда один большой запрос предпочтительнее многих небольших запросов. Django использует большой запрос для предварительного создания моделей в памяти, а не для выполнения запросов по требованию к базе данных.
select_related
выполняет соединение с каждым поиском, но расширяет выбор, чтобы включить столбцы всех соединенных таблиц. Однако у этого подхода есть одна оговорка.Объединения могут умножить количество строк в запросе. Когда вы выполняете объединение по внешнему ключу или однозначному полю, количество строк не увеличивается. Однако объединения «многие ко многим» не имеют такой гарантии. Таким образом, Django ограничивается
select_related
отношениями, которые не могут неожиданно привести к массовому объединению.«Присоединиться питон» для
prefetch_related
немного более тревожной , то это должно быть. Он создает отдельный запрос для каждой таблицы, к которой нужно присоединиться. Он фильтрует каждую из этих таблиц с предложением WHERE IN, например:Вместо того, чтобы выполнять одно соединение с потенциально слишком большим количеством строк, каждая таблица разбивается на отдельный запрос.
источник
Как сказано в документации Django:
Подробнее об этом: https://docs.djangoproject.com/en/2.2/ref/models/querysets/#prefetch-related
источник
Перебрал уже выложенные ответы. Просто подумал, что будет лучше, если я добавлю ответ с реальным примером.
Допустим, у вас есть 3 модели Django, которые связаны между собой.
Здесь вы можете запросить
M2
модель и ее относительныеM1
объекты, используяselect_relation
поле, иM3
объекты, используяprefetch_relation
поле.Однако, как мы уже упоминали
M1
, отношение fromM2
является aForeignKey
, оно просто возвращает только 1 запись для любогоM2
объекта. То же самое относится и кOneToOneField
.Но
M3
соотношение «S отM2
это ,ManyToManyField
которое может возвращать любое количествоM1
объектов.Рассмотрим случай, когда у вас есть 2
M2
объектаm21
,m22
которые имеют одинаковые 5 связанныхM3
объектов с идентификаторами1,2,3,4,5
. Когда вы выбираете связанныеM3
объекты для каждого из этихM2
объектов, если вы используете select related, это то, как это будет работать.шаги:
m21
объект.M3
объекты, связанные сm21
объектом, чьи идентификаторы1,2,3,4,5
.m22
объекта и всех другихM2
объектов.Так как мы имеем одинаковые
1,2,3,4,5
идентификаторы для обоихm21
,m22
объекты, если мы используем select_related вариант, он будет запрашивать БД дважды для одних и тех же идентификаторов , которые уже были извлечены.Вместо этого, если вы используете prefetch_related, когда вы пытаетесь получить
M2
объекты, он запомнит все идентификаторы, которые ваши объекты вернули (Примечание: только идентификаторы) при запросеM2
таблицы и в качестве последнего шага, Django собирается сделать запрос кM3
таблице с набором всех идентификаторов, которые вашиM2
объекты вернули. и присоединить их кM2
объектам, используя Python вместо базы данных.Таким образом, вы запрашиваете все
M3
объекты только один раз, что повышает производительность.источник