Давайте изучим Django Queryset API более подробно.
Составление эффективных запросов к базе данных – один из самых важных навыков в работе любого бэкенда. Запросы могут либо сделать ваше приложение, либо уничтожить его. Оптимизация вашего приложения с бэкенда заключается в написании эффективных алгоритмов. Разработчики Django потратили достаточно времени на написание этих сложных алгоритмов для повторного использования другими разработчиками. Если вам, как и мне, интересно узнать, что происходит. Вам следует изучить проект django на Github.
Сегодня мы сосредоточимся на приложении на базе Django и на том, как мы можем использовать встроенный API queryset, чтобы ускорить не только время разработки, но и время отклика вашего приложения.
Наши модели
from django.db import models
from django.conf import settings
class BaseModel(models.Model):
created_at = models.DateTimeField(auto_now_add=True)
modified_at = models.DateTimeField(auto_now=True)
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
class Meta:
abstract = True
class Investment(BaseModel):
title = models.CharField(max_length=200)
interest = models.DecimalField(decimal_places=2, max_digits=4)
min_invest = models.DecimalField(decimal_places=2, max_digits=8)
max_invest = models.DecimalField(decimal_places=2, max_digits=8)
duration = models.DurationField()
active = models.BooleanField(default=False)
def __str__(self) -> str:
return self.title
class Meta:
verbose_name_plural = "Investment"
ordering = "-created_at"
class UserInvest(BaseModel):
amount = models.DecimalField(max_digits=4, decimal_places=2)
package = models.ForeignKey(Investment, on_delete=models.PROTECT)
started = models.BooleanField(default=False)
def __str__(self) -> str:
return f"{self.user.username} invested {self.amount} in {self.package.title) package"
''' other methods for comes in.
I love writing fat models because it makes my work easy
and makes the application code to have one source of truth
Note: You should always use django.utils.translation for translation.
'''
Наши представления
Здесь начинается самое интересное. Мы затронем каждую часть queryset api и покажем вам более сложные методы запросов к базе данных.
Получите все запросы в обратном порядке.
'''
We will make database queries through our models.
Note: we assume that we have enough data in our database to work with.
'''
from django.views.generic import ListView
from django.db.models.query import Q
from myapp.investment.models import Investment, UserInvest
class InvestmentListView(ListView):
template_name = "investment/investment_list.html"
model = Investment
context_object_name = "investment"
def get_queryet(self, **kwargs):
return super().get_queryset(self, kwargs).order_by("-created_at")
Запрос выше перечисляет все инвестиционные пакеты, которые есть у компании, начиная с самого последнего. Django предлагает эффективный способ оптимизации. Вместо использования встроенных методов python, django предоставляет хороший api для оптимизации такого запроса.
Примечание: Не используйте встроенные методы python, если django предоставил альтернативу. вместо reverse(queries) используйте
queries.order_by() или передайте ключевые слова упорядочивания в мета-классе модели.
#You can pass in the ordering keywords with the model to re-order your data. like
class Meta:
ordering = "-created_at"
# OR use
def get_querset(self, **kwargs):
return super().get_queryset(self, kwargs).order_by("-created_at")
Альтернативы распространенным запросам
вместо использования this для проверки существования объектов.
for investment in Investment.objects.all():
if investment.title == "UBA stock":
print(f"investment.title exists")
Сделать это
Investment.objects.filter(title="UBA stock").exist()
Для подсчета объектов используйте
Investment.objects.count()
Для выделения нескольких первых/последних элементов используйте нарезку
Investment.objects.all()[:5]
Чтобы использовать условия при получении объектов, попробуйте отфильтровать их по такому условию,
from django.db.model.query import Q
Investment.objects.filter(active=True, created_at__month=datetime.datetime.now().month)
## complex queries use Q objects
invest = Q(Investment.objects.filter(active=True) | Investment.objects.filter(completed=True))
Чтобы исключить некоторые объекты с определенными условиями, попробуйте следующее
Investment.objects.exclude(created_at__gt=datetime.date(2022, 6, 2), title='Morgan Stock')
NB: Вы можете объединить эти отдельные запросы в цепочку для выполнения более сложных запросов.
Чтобы отменить запросы
Investment.objects.reverse()
Чтобы получить запросы, которые имеют разные значения
Investment.objects.order_by('created_at').distinct('created_at')
Чтобы выполнить запрос по значениям
Investment.objects.filter(title__istartswith="UBA")
Investment.objects.filter(title__iendswith="stock")
Investment.objects.filter(title__icontains="stock")
## These are case sensitive operations, so i prefer using i to prefix the startswith or endswith
Что из дат
>>> Investment.objects.dates('created_at', 'year')
[datetime.date(2022, 1, 1)]
>>> Investment.objects.dates('created_at', 'month')
[datetime.date(2022, 2, 1), datetime.date(2022, 3, 1)]
>>> Investment.objects.dates('created_at', 'week')
[datetime.date(2022, 2, 14), datetime.date(2022, 3, 14)]
>>> Investment.objects.dates('created_at', 'day')
[datetime.date(2022, 2, 20), datetime.date(2022, 3, 20)]
>>> Investment.objects.dates('created_at', 'day', order='DESC')
[datetime.date(2022, 3, 20), datetime.date(2022, 2, 20)]
>>> Investment.objects.filter(title__contains='UBA').dates('pub_date', 'day')
[datetime.date(2022, 3, 20)]
Если вы хотите объединить несколько разных запросов, используйте объединение
invest1 = Investment.objects.filter(active=True)
invest2 = Investment.objects.filter(completed=True)
invest3 = Investment.objects.filter(active=False)
invest = invest1.union(invest2)
invest4 = invest1.union(invest2, invest3)
Чтобы получить пересечение двух запросов, используйте
invest1 = Investment.objects.filter(active=True)
invest2 = Investment.objects.filter(completed=True)
invest3 = Investment.objects.filter(active=False)
invest = invest1.intersection(invest2)
Чтобы получить разность двух запросов, используйте
invest1 = Investment.objects.filter(active=True)
invest2 = Investment.objects.filter(completed=True)
invest3 = Investment.objects.filter(active=False)
invest = invest1.difference(invest2)
Если у вас есть объекты, между которыми есть связи, и вы не хотите делать несколько запросов к базе данных для получения связей, используйте функцию select_related или prefetch_related
UserInvest.objects.select_related("package").get(id="7")
UserInvest.objects.filter(amount__gte=200).prefetch_related("package")
UserInvest.objects.filter(amount__gte=200).select_related("package")
Есть много вещей, происходящих при использовании select_related или prefetch related. Я предлагаю вам посмотреть документацию django и понять, как они обе выполняют свои запросы.
Вместо того, чтобы писать сырые запросы, используя объекты курсора, используйте django select и дополнительную функцию
Investment.objects.extra(select={'is_recent': "created_at > '2022-01-01'"})
UserInvest.objects.extra(
select={
'entry_count': 'SELECT COUNT(*) FROM userinvest_entry WHERE userinvest_entry.userinvest_id = userinvest_userinvest.id'
},
)
## This query might not make much sense, but it shows what is possible.
В некоторых сложных ситуациях моделирования данных ваши модели могут содержать множество полей, некоторые из которых могут содержать большое количество данных (например, текстовые поля) или требовать дорогостоящей обработки для преобразования их в объекты Python. Если вы используете результаты набора запросов в ситуации, когда вы не знаете, нужны ли вам эти конкретные поля при первоначальном получении данных, вы можете сказать Django не извлекать их из базы данных. используйте defer в этом случае
UserInvest.objects.defer("package")
Если вы хотите отложить все остальные поля, кроме некоторых, используйте only.
Investment.objects.only("title", "duration").only("user")
Если вы подключены к нескольким базам данных и хотите использовать определенную базу данных, используйте using для указания базы данных.
Investment.objects.using('backup')
К первому или последнему запросу
p = Investment.objects.order_by('title', 'created_at').first()
q = Investment.objects.order_by('title', 'created_at').last()
Чтобы подсчитать или суммировать поле, используйте функцию Агрегат и Сумма
from django.db.models.aggregates import Sum, Count
q = Investment.objects.aggregate(Count('title'))
total_amount = UserInvest.objects.aggregate(Sum("amount"))
Django предоставляет множество api для выполнения высоко оптимизированных запросов. Все, что вам нужно сделать, это прочитать документацию
Счастливого кодинга!!!