Глубокое погружение в эффективные запросы к базе данных
Скорость, с которой работает ваше приложение django, во многом зависит от скорости, с которой приложение может получать результаты из базы данных. Несмотря на то, что существуют различные способы оптимизации вашего приложения django, в этой статье будет рассказано, как вы можете использовать возможности наборов запросов для получения правильных результатов.
В этом руководстве мы будем использовать модель продукта для интернет-магазина.
class Product(models.Model): RATING_CHOICES = [ ('*****', '5'), ('****', '4'), ('***', '3'), ('**', '2'), ('*', '1'), ] name = models.CharField(max_length=200) category = models.CharField(max_length=200) price = models.DecimalField(max_digits=8, decimal_places=2) discounted_price = models.DecimalField(max_digits=8, decimal_places=2, null=True, blank=True) description = models.TextField() image = models.ImageField(upload_to='products/', null=True,blank=True) rating = models.CharField(max_length=5, choices=RATING_CHOICES, null =True, blank=True) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True)
Напишем команду управления, которая добавляет в базу данных 50 товаров. Добавьте файл product.py в каталог команд внутри пакета управления.
.ecommerce ├── admin.py ├── apps.py ├── __init__.py ├── management │ ├── commands │ │ ├── __init__.py │ │ └── product.py │ └── __init__.py
Добавьте следующий код в файл product.py.
from django.core.management.base import BaseCommand from ecommerce.models import Product from random import choice, randint class Command(BaseCommand): help = 'Add 100 products' def handle(self, *args, **options): for _ in range(50): name = "Product {}".format(randint(1, 50)) category = "Fashion" price = randint(10, 100) description = "The generated Lorem Ipsum is therefore always free from repetition, injected humour, or non-characteristic words etc." rating = choice(Product.RATING_CHOICES)[0] product = Product( name=name, category=category, price=price, description=description, rating=rating ) product.save() self.stdout.write(self.style.SUCCESS('products added'))
Чтобы запустить команду, скрипт, введите следующую команду
python manage.py product
Если вам нужно добавить больше продуктов в другую категорию, измените название категории и повторите команду.
Понимание наборов запросов
Наборы запросов в Django похожи на контейнеры, содержащие информацию из базы данных. Наборы запросов упрощают сортировку данных, внесение изменений и получение необходимой информации. Думайте о наборах запросов как об упрощенном способе работы с базами данных в Django. Они обрабатывают сложные части за вас, поэтому вы можете сосредоточиться на использовании простого и знакомого кода Python для взаимодействия с базой данных.
Получение объектов
Django использует атрибут объектов для представления менеджера, который управляет взаимодействием между базой данных и моделями.
Чтобы получить все объекты продукта, используйте метод all()
.
>>> products = Product.objects.all() >>>
Этот запрос вернет набор запросов, содержащий все объекты Product.
Вы можете настраивать запросы, упорядочивая, разделяя или фильтруя запрос. Например, чтобы получить продукты с определенной категорией, используйте метод filter()
:
>>> products = Product.objects.filter(category='Fashion')
Используйте метод order_by()
, чтобы указать критерии для упорядочения извлеченных объектов модели. Например, давайте создадим запрос, который упорядочивает товары по цене в порядке убывания.
>>> products = Product.objects.order_by('price') #objects sorted based on their price, from lowest to highest. >>> [p.price for p in products] [Decimal('3.00'), Decimal('10.00'), Decimal('10.00'), Decimal('15.00'), Decimal('19.00'),...)] >>>
Используйте метод нарезки, чтобы получить последние пять продуктов в базе данных.
>>> products = Product.objects.order_by('-created_at')[:5]
Приведенный выше запрос использует поле created_at
для получения последних пяти продуктов, добавленных в базу данных; порядок -created_at
гарантирует, что самые последние продукты появляются первыми в наборе запросов.
Наборы запросов Django ленивы:
Django QuerySets использует ленивую загрузку, что означает, что фактический запрос к базе данных будет выполняться только тогда, когда вам понадобятся данные. Эта отложенная оценка позволяет комбинировать несколько фильтров, аннотаций и порядков без немедленной выборки данных из базы данных. Это ленивое вычисление помогает свести к минимуму ненужные операции с базой данных и повысить производительность за счет доступа к базе данных только при необходимости.
Например, рассмотрим следующий набор запросов, включающий цепочку из нескольких фильтров:
>>> filtered_products = Product.objects.filter(rating = '5' ).order_by("-price") >>> len(connection.queries) 0 >>>
На данный момент набор запросов еще не был оценен, а количество запросов равно 0; когда мы определяем набор запросов, набор запросов действует как «ленивый» объект, который содержит информацию о запросе. Запрос не попадет в базу данных, пока мы не выполним действие, такое как повторение набора запросов, доступ к определенным элементам или выполнение таких методов, как `count()` или `get()`.
>>> [f" The price of {product.name} is ${product.price}" for product in filtered_products] [' The price of Product 280 is $95.00', ' The price of Product 125 is $91.00', ' The price of Product 761 is $91.00', ' The price of Product 87 is $88.00', ' The price of Product 573 is $87.00', ' The price of Product 616 is $80.00', ' The price of Product 611 is $78.00', ' The price of Product 31 is $77.00', ' The price of Product 433 is $73.00', ' The price of Product 348 is $73.00', ' The price of Product 651 is $62.00', ' The price of Product 121 is $62.00', ' The price of Product 859 is $61.00', ' The price of Product 163 is $56.00', ' The price of Product 718 is $50.00', ' The price of Product 32 is $46.00', ' The price of Product 415 is $44.00', ' The price of Product 591 is $37.00', ' The price of Product 414 is $34.00', ' The price of Product 926 is $29.00', ' The price of Product 822 is $28.00', ' The price of Product 459 is $28.00', ' The price of Product 691 is $27.00', ' The price of Product 781 is $17.00'] >>> len(connection.queries) 1 >>>
Наборы запросов кэшируются
В Django наборы запросов кэшируются, чтобы уменьшить доступ к базе данных. Когда набор запросов оценивается в первый раз и результат извлекается из базы данных, Django сохраняет результат в кеше наборов запросов
Однако немедленные будущие оценки того же набора запросов в рамках одного и того же цикла запроса/ответа будут использовать кешировать результат вместо повторного обращения к базе данных.
Давайте рассмотрим пример с использованием модели Product:
>>> products = Product.objects.filter(rating = '5' ).order_by("-price") >>> [product.name for product in filter_prodcuts] #this hits the database >>> len(connection.queries) 1 >>> [product.price for product in products] # this uses the cached data >>> len(connection.queries) # number of queries remain the same 1
Важно отметить, что кэш набора запросов зависит от состояния базовых данных. Предположим, что данные изменились; например, новая запись сохраняется в течение того же цикла запроса/ответа.
Кэш наборов запросов устаревает, и новый запрос оценивается для обеспечения наиболее актуальных результатов.
Поиск полей
Django также поддерживает поиск полей для использования в запросах.
exact
: используется для выполнения запроса на точное соответствие, используйте нотацию с двойным подчеркиванием (__), за которой следует имя поля. Например, чтобы получить продукты с определенным названием,
>>> product=Product.objects.filter(name__exact='Product 12') >>> product <QuerySet [<Product: Product 12>]> >>>
iexact
:используется для выполнения точного cнечувствительного к ASE совпадения
>>> product = Product.objects.filter(name__iexact='PRODUCT 12') >>> product <QuerySet [<Product: Product 12>]> # Product 12 is matched even though the query is PRODUCT 12 >>>
Продукт 12 соответствует, хотя запрос является ПРОДУКТ 12
Поиск полей сравнения
Django поддерживает различные поисковые операции сравнения, такие как:
- больше, чем (gt)
- больше или равно (gte)
- меньше чем (л)
- меньше или равно (lte).
Например, давайте получим товары с ценой меньше 20:
>>> from ecommerce.models import Product >>> products = Product.objects.filter(price__lt=20) >>> products <QuerySet [<Product: Product 26>, <Product: Product 27>, <Product: Product 15>, <Product: Product 24>, <Product: name>]> >>>
Поиск полей даты и времени. Django предоставляет полезные возможности поиска полей даты и времени. Вы можете использовать поиск по дате, году, месяцу, дню, часу, минуте и секунде, чтобы фильтровать товары по определенным датам или времени.
Например, давайте получим продукты, созданные в текущем году:
>>> from django.utils import timezone >>> products=Product.objects.filter(created_at__year= timezone.now().year) >>> products <QuerySet [<Product: Product 17>, <Product: Product 13>, <Product: Product 2>, <Product: Product 8>, <Product: Product 28>, <Product: Product 21>, <Product: Product 11>, <Product: Product 32>, <Product: Product 28>, <Product: Product 16>, <Product: Product 26>, <Product: Product 22>, <Product: Product 34>, <Product: Product 26>, <Product: Product 22>, <Product: Product 28>, <Product: Product 20>, <Product: Product 48>, <Product: Product 27>, <Product: Product 33>, '...(remaining elements truncated)...']> >>>
Агрегации и аннотации:
Наборы запросов поддерживают различные функции агрегирования, такие как count()
, sum(),
avg()
и аннотации с использованием annotate()
.
Эти функции позволяют выполнять вычисления непосредственно в базе данных, а не в коде Python, оптимизируя производительность запросов.
Например, вы можно использовать avg() t
o для получения средней цены продуктов в определенной категории без необходимости выбирать отдельные продукты
>>> avg_price = Product.objects.filter(category='Fashion').aggregate(avg_price=Avg('price')) >>> print(avg_price['avg_price']) 53.1200000000000 >>>
Оштрафуйте продукт с самой высокой ценой с помощью Max
>>> from django.db.models import Max >>> max_price = Product.objects.aggregate(max_price=Max('price')) >>> print(f"The highest price of a product is {max_price['max_price']}") The highest price of a product is 100
Заключение
В этой статье были рассмотрены некоторые основные концепции ORM django. Прочтите эту статью, чтобы узнать больше о том, как выполнять оптимизацию с использованием других методов, таких как предварительная выборка, индексация и т. д.
Повышение уровня кодирования
Спасибо, что являетесь частью нашего сообщества! Перед тем, как ты уйдешь:
- 👏 Хлопайте за историю и подписывайтесь на автора 👉
- 📰 Смотрите больше контента в публикации Level Up Coding
- 💰 Бесплатный курс собеседования по программированию ⇒ Просмотреть курс
- 🔔 Подписывайтесь на нас: Twitter | ЛинкедИн | "Новостная рассылка"
🚀👉 Присоединяйтесь к коллективу талантов Level Up и найдите прекрасную работу