В чем разница между select related и prefetch related в Django ORM?

в Django doc,

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

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

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

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

2 ответов


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

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 server. prefetch_related С другой стороны выполняет другой запрос и, следовательно, уменьшает избыточные столбцы в исходном объекте (ModelA в приведенном выше примере). Вы можете использовать prefetch_related за все, что вы можете использовать select_related for.

компромиссы таковы prefetch_related должен создать и отправить список идентификаторов, чтобы выбрать обратно на сервер, это может занять некоторое время. Я не уверен, что есть хороший способ сделать это в транзакции, но я понимаю, что Django всегда просто отправляет список и говорит ВЫБИРАТЬ... Где pk в (...,...,...) в основном. В этом случае, если предварительные данные разрежены (скажем, объекты состояния США, связанные с адресами людей), это может быть очень хорошо, однако, если это ближе к Один-к-одному, это может привести к потере много коммуникаций. Если сомневаетесь, попробуйте оба и посмотрите, что работает лучше.

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


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

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

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

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

на "присоединяйтесь к 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);

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