Сегодня #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://docs.djangoProject.com/ru/1.11/ref/models/Querysets/
- https://docs.djangoproject.com/en/1.11/ref/models/querysets/#prefetch-related
- https://docs.djangoProject.com/ru/1.11/REF/Models/Querysets/#django.db.models.query.queryset.select_related.
Увидимся позже, продолжайте кодирование и веселиться ❤
Оригинал: “https://dev.to/lucasmagnum/djangotip-select-prefetch-related-402i”