Рубрики
Без рубрики

#Djangotip: выберите и prefetch_related

Сегодня #djangotip будет использоваться с использованием select_related и prefetch_related для улучшения наших запросов p … Теги с Python, Django.

Сегодня #Djangotip будет о использовании select_related и Prefetch_related Улучшить производительность запросов.

Вы можете клонировать этот проект от [http://github.com/lucasmagnum/django-tip-02](http://github.com/lucsmagnum/django-tip-02) и следуйте примерам ниже.

Требуется время, чтобы полностью понять, как select_related и Prefetch_related Работает, поэтому продолжайте искать другие ресурсы, и я надеюсь, что этот пост поможет вам понять немного больше. Код и повеселиться ❤

Прежде чем мы начнем, позвольте мне задать вам один вопрос; Вы хлопали сегодня? Я хотел бы услышать ваши хлопья и прочитать ваши отзывы!

Модель продукта

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

https://github.com/LucasMagnum/django-tip-02/blob/master/app/products/models.py

Это довольно просто, верно? Продукт имеет Иностранные ключевые отношения с Категория и Категория имеет один Многие для многих отношений с собой.

Давайте начнем, будем ли мы?

Список продуктов

Начнем создать список функций продуктов, эта функция вернет один список диктопов с полями: ID , заглавие и категория название:

https://github.com/LucasMagnum/django-tip-02/blob/master/app/products/queries.py#L128

Для проверки нашей функции нам нужны продукты в нашей базе данных. У нас есть одна команда для загрузки продуктов в базу данных, просто выполнить load_products Команда и 500 продуктов и 50 категорий будут вставлены в нашу базу данных.

Python App/manage.py load_products

Теперь мы открываем наши оболочка Бег Python App/Manage.py Shell и проверить нашу функцию:

У нас есть все наши продукты, но в чем проблема с этой функцией? Давайте посмотрим, сколько запросов эта функция делает в базу данных:

Примечание: Я создал один Отладчик декоратор Чтобы увидеть, сколько запросов каждый пример требует, вам не нужно понимать, как это работает, просто имейте в виду, что он показывает количество запросов и время в секундах для каждого примера: P

https://gist.github.com/LucasMagnum/81a122b7317baccf7c6bb4c9c549c928

Вау! 501 запросы для этого простого куска кода ?? Что случилось?

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

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

for product in product_qs:
   products.append({
       'id': product.id,
       'title': product.title,
       'category': product.category.name
   })

Чтобы избежать этого большого количества запросов при доступе к Иностранные ключи или один на один полей Мы можем использовать select_related метод.

Выбирать Связанный

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

Давайте изменим наш код, чтобы использовать select_related С нашей продукцией Queryset:

https://github.com/LucasMagnum/django-tip-02/blob/master/app/products/queries.py#L148

Теперь мы можем проверить, что эти две функции возвращают одни и те же значения и посмотрите, сколько запросов Products_list_select_related делает:

https://gist.github.com/LucasMagnum/5d5e54e80a7ec4b1c1b399e65081a4c4

Поздравляю! Теперь мы сделали только 1 запрос вместо 501, это настоящее улучшение: D

Список категорий

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

Давайте создадим функцию списка категорий, чтобы получить все категории и их подкатегории. Эта функция должна вернуть Имя , Is_active и а подкатегории Список для каждой категории.

https://github.com/LucasMagnum/django-tip-02/blob/master/app/products/queries.py#L7

Если мы запустим эту функцию в режиме отладчика, мы увидим, сколько запросов эта функция делает:

https://gist.github.com/LucasMagnum/b35cb3d6158644ebbc40ca7e345896ed

Хорошо, мы увидели эту проблему раньше, нам просто нужно добавить select_related И мы закончим только один запрос, верно? Err, нет!

Если мы попытаемся изменить Категории_Qs использовать select_related Мы получим ошибку из Django:

Исключение При попытке использовать SELECT_RELATED в многочисленном поле для многих

Мы не можем использовать select_related с Многие для многих отношений , ты помнишь? Чтобы улучшить наши вопросы, нам нужно использовать новый метод под названием Prefetch_related Отказ

Предварительная выборка связана

Когда мы используем Многие для многих отношений Мы могли бы использовать Prefetch_related метод усилинности нашего

Давайте создадим другую функцию, используя Prefetch_related Чтобы увидеть наши улучшения:

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

Мы только что изменили строку 5, чтобы использовать Prefetch_related на подкатегории поле. Теперь давайте посмотрим, сколько запросов эта новая функция делает:

https://gist.github.com/LucasMagnum/efe74895552ebd24c5897db90b81712a

Молодец! Мы улучшили производительность нашего запроса, но подождите секунду, почему у нас есть Два Запросы?

Отличается от select_related Prefetch_related сделал соединение с помощью Python, а не в базе данных.

В этом случае Django сделал два запроса, а затем присоединяйтесь к результатам в один запрос для нас.

Давайте создадим новую функцию, которая вернет только активные подкатегории, используя Prefetch_Related:

Список категорий с использованием функции связанной с префектом.

Теперь, когда мы запускаем отладчик для этой функции, мы видим:

https://gist.github.com/LucasMagnum/f5022fc09830a21c534e8ca1f2b8bd92

ВАУ! Мы увеличили количество запросов, как это возможно ????? Успокойся, есть причина для этого:

Когда мы используем Предварительная выборка связана Мы говорим Django, мы хотим, чтобы все результаты были присоединены, но когда мы используем Фильтр (IS_ Accave = True) Мы изменяем первичный запрос, а затем Django не присоединятся к нам подходящие результаты.

Вот почему у нас есть 52 запрос, 51 запросы, итерацию по категориям и 1 запрос, чтобы получить все результаты в предварительной выборке.

Давайте посмотрим, как решить эту проблему, используя новый Предварительная выборка введен недавно Django (;

Используя предварительную выборку с to_attr

Prefetch, используя To_attr, чтобы вернуть значения, связанные с префектом

Есть два изменения в этой новой функции, я изменил Категории_Qs Запрос использовать пользовательский Предварительная выборка и изменил цикл, чтобы использовать новый атрибут active_subcategories Отказ

Давайте понять, что это делает:

Category.objects.prefetch_related(
    Prefetch(
         'subcategories',
          queryset=Category.objects.filter(is_active=True),
          to_attr='active_subcategories'
    )
)

Мы предварительно выбираем все значения из подкатегории поле, используя Catework.Objects.filter (iS_активно = true) как Базовая критация и проезжая сюда Django, мы хотим, чтобы все предварительные значения в атрибут active_subcategories Отказ Это создаст атрибут Active_SubCategories для каждого категория вернулся нашим Queryset Отказ

Мы видим это в нашем Django Shell :

https://gist.github.com/LucasMagnum/a13afb6ad657136d7c17f5e53d2a6f68

Как мы видим, когда мы пытаемся получить доступ к Active_SubCategories Не заставляя связанную с префитом, мы получим ошибку.

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

Теперь мы сделаем новый запрос для предварительной защиты всех результатов;

YEES !! Теперь мы можем немного лучше понять, как новый Предварительная выборка работает.

Давайте отладим нашу функцию сейчас:

https://gist.github.com/LucasMagnum/f6060fe8bc0a1390aed0e27439eb029b

Здорово!! Теперь у нас есть только два запроса, а не

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

Используя префиту без to_attr

Если мы не хотим создавать новый атрибут, мы можем повторно использовать атрибут с поля и просто изменить наш базовый запрос:

https://gist.github.com/LucasMagnum/21074818968d99d7507d24526fa63818

Здесь мы используем Предварительная выборка без указания атрибута. Когда мы доступ к Cature.SubCategories. Мы используем значения из Предварительная выборка поэтому нам не нужно фильтровать is_active. Вот потому что результаты будут отфильтрованы =)

И если мы отладим эту функцию, у нас все еще есть только два запроса:

https://gist.github.com/LucasMagnum/5bda88c72978ad4679b8062a5cd7d6b0

Мы можем проверить, возвращают ли ваши функции одинаковых значений:

https://gist.github.com/LucasMagnum/65425f73807da299b08acac8ef393daa

ФАНТАСТИКА!!! Все работает, и я надеюсь, что вы узнали немного больше о новой префити сегодня =)

TL; DR;

  • Select_related и prefetch_related – повышение производительности
  • Выберите, связанный с одним запросом, чтобы присоединиться к таблицам
  • Сбросьте связанные могут использоваться с Иностранником и OneToonefield
  • Выбрать связанные не работает со множеством полей
  • Связанная префектура делает два запроса и присоединяйтесь к Python
  • Предварительная выборка теряет его эффект, когда вы меняете базовый запрос

Полезные ссылки

Увидимся позже, продолжайте кодирование и веселиться ❤

Оригинал: “https://dev.to/lucasmagnum/djangotip-select-prefetch-related-402i”