Глубокое погружение в эффективные запросы к базе данных
Скорость, с которой работает ваше приложение 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() to для получения средней цены продуктов в определенной категории без необходимости выбирать отдельные продукты
>>> 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 и найдите прекрасную работу