← Назад к вопросам
Выполнял ли сложные запросы в Django ORM
2.2 Middle🔥 161 комментариев
#Django#Базы данных (SQL)
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Выполнял ли сложные запросы в Django ORM
Да, много. Django ORM может быть как простым так и сложным.
Простые запросы
# Базовые CRUD
User.objects.all()
User.objects.get(id=1)
User.objects.filter(email='test@example.com')
User.objects.create(name='John', email='john@example.com')
Сложные запросы которые я делал
1. Аннотирование и агрегирование
from django.db.models import Count, Sum, Avg, Q
# Сколько постов у каждого пользователя
users = User.objects.annotate(
post_count=Count('posts')
).filter(
post_count__gt=5 # Больше 5 постов
)
# Средняя оценка поста по авторам
authors = User.objects.annotate(
avg_rating=Avg('posts__rating')
).order_by('-avg_rating')
# Сумма всех заказов по категориям
categories = Category.objects.annotate(
total_sales=Sum('products__orders__amount')
)
2. Сложные фильтры с Q объектами
from django.db.models import Q
# Пользователи которые либо админы либо создали >10 постов
users = User.objects.filter(
Q(is_admin=True) | Q(posts__count__gt=10)
).distinct()
# Исключающие условия (NOT)
posts = Post.objects.filter(
Q(published=True) & ~Q(status='draft')
)
# Комплексная логика
orders = Order.objects.filter(
(Q(status='pending') & Q(created_at__lt=yesterday)) |
(Q(status='processing') & Q(created_at__lt=last_week))
)
3. Select_related и prefetch_related
# Без оптимизации = N+1 проблема
posts = Post.objects.all()
for post in posts:
print(post.author.name) # Каждый раз новый SQL запрос!
# С select_related (для ForeignKey)
posts = Post.objects.select_related('author', 'category')
for post in posts:
print(post.author.name) # Уже загружено в памяти
# С prefetch_related (для reverse ForeignKey и ManyToMany)
authors = User.objects.prefetch_related('posts')
for author in authors:
for post in author.posts.all(): # Уже загружено
print(post.title)
# Custom Prefetch для фильтрования
from django.db.models import Prefetch
published_posts = Post.objects.filter(published=True)
authors = User.objects.prefetch_related(
Prefetch('posts', queryset=published_posts)
)
4. F выражения
from django.db.models import F, Value
from django.db.models.functions import Concat
# Обновить поле основываясь на другом поле
Order.objects.all().update(
total_price=F('price') * F('quantity')
)
# Увеличить значение
Product.objects.filter(id=1).update(
views=F('views') + 1
)
# Конкатенировать строки
users = User.objects.annotate(
full_name=Concat(
F('first_name'),
Value(' '),
F('last_name')
)
)
5. Сложные JOIN'ы и через несколько таблиц
# Orders где товар из категории X и статус Y
orders = Order.objects.filter(
items__product__category__name='Electronics',
status='completed'
).distinct()
# Все посты которые лайкнула девушка
posts = Post.objects.filter(
likes__user__gender='female'
).distinct()
# Двойная связь через m2m
authors_who_collab = Author.objects.filter(
books__contributors=Author.objects.get(name='Alice')
).exclude(
id=Author.objects.get(name='Alice').id
)
6. Case/When для условной логики
from django.db.models import Case, When, Value, IntegerField
# Скор на основе условий
users = User.objects.annotate(
score=Case(
When(is_admin=True, then=Value(100)),
When(posts__count__gte=50, then=Value(80)),
When(posts__count__gte=10, then=Value(50)),
default=Value(0),
output_field=IntegerField()
)
)
# Категория на основе цены
products = Product.objects.annotate(
price_category=Case(
When(price__lt=100, then=Value('cheap')),
When(price__lt=1000, then=Value('medium')),
default=Value('expensive')
)
)
7. Values и Values_list
# Только конкретные поля (для экономии памяти)
user_names = User.objects.values_list('id', 'name')
# [1, 'John'], [2, 'Jane']
# Как словари
users = User.objects.values('id', 'name', 'email')
# [{'id': 1, 'name': 'John', 'email': 'john@example.com'}]
# С аннотирование
top_users = User.objects.values('id', 'name').annotate(
post_count=Count('posts')
).order_by('-post_count')[:10]
8. Raw SQL когда ORM не может
# Если ORM слишком сложный - raw SQL
users = User.objects.raw('''
SELECT u.id, u.name, COUNT(p.id) as post_count
FROM users u
LEFT JOIN posts p ON u.id = p.user_id
WHERE p.created_at > %s
GROUP BY u.id, u.name
ORDER BY post_count DESC
''', [start_date])
# Или через extra (но лучше raw)
posts = Post.objects.extra(
select={'author_posts': 'SELECT COUNT(*) FROM posts WHERE user_id=posts.user_id'}
)
# Или через cursor для очень сложного
from django.db import connection
with connection.cursor() as cursor:
cursor.execute('SELECT ...', [params])
result = cursor.fetchall()
9. Оптимизация с only/defer
# Загрузить только нужные поля
users = User.objects.only('id', 'name')
# Не загружает password, email и т.д.
# Загрузить всё кроме больших полей
posts = Post.objects.defer('content')
# Загружает title, author но не content
10. Distinct и Group By
# Уникальные авторы которые написали о Django
authors = User.objects.filter(
posts__tags__name='django'
).distinct()
# Не то же самое что GROUP BY!
# GROUP BY требует aggregate в SELECT
from django.db.models import Count
authors = User.objects.filter(
posts__tags__name='django'
).values('id', 'name').annotate(
post_count=Count('id')
).order_by('-post_count')
Ошибки которые я совершал
Ошибка 1: N+1 запросы
# ПЛОХО
posts = Post.objects.all()
for post in posts:
print(post.author.name) # 1000 запросов!
# ХОРОШО
posts = Post.objects.select_related('author')
for post in posts:
print(post.author.name) # 1 запрос
Ошибка 2: Неправильный distinct
# ПЛОХО - не удаляет дубли
posts = Post.objects.filter(
likes__user=user
).distinct() # Не помогает
# ХОРОШО - сначала фильтруем потом distinct
posts = Post.objects.filter(
likes__user=user
).select_related('author').distinct()
Ошибка 3: Слишком много аннотирований
# ПЛОХО - медленно
users = User.objects.annotate(
post_count=Count('posts'),
comment_count=Count('comments'),
like_count=Count('likes'),
follower_count=Count('followers')
)
# ХОРОШО - кэшируем в отдельные поля
# Или считаем в приложении отдельно
Когда переходить на Raw SQL
# Используй ORM когда:
# - Простые запросы
# - Нужна переносимость БД
# - Есть встроенные методы
# Используй Raw SQL когда:
# - Очень сложные запросы
# - Нужна максимальная производительность
# - ORM не может это выразить
# - Используешь специфичные для БД функции
Инструменты для дебага
from django.db import connection
# Посмотри какой SQL сгенерировал ORM
print(Post.objects.filter(author__name='John').query)
# Посмотри все запросы в запросе
from django.db import connection
from django.test.utils import override_settings
@override_settings(DEBUG=True)
def my_view():
# ...код...
print(len(connection.queries)) # Количество запросов
for q in connection.queries:
print(q['sql']) # Сам SQL
print(q['time']) # Время выполнения
Реальные примеры из проектов
1. Рекомендации для пользователей
from django.db.models import Q, Count
# Товары которые нравятся друзьям пользователя
def get_recommendations(user):
return Product.objects.filter(
likes__user__in=user.friends.all(),
~Q(likes__user=user) # Не лайкнул сам
).annotate(
score=Count('likes')
).order_by('-score')[:10]
2. Статистика по времени
from datetime import timedelta
from django.utils import timezone
from django.db.models import Count
# Активные пользователи за последнюю неделю
week_ago = timezone.now() - timedelta(days=7)
active_users = User.objects.filter(
last_activity__gte=week_ago
).annotate(
action_count=Count('actions')
).filter(
action_count__gte=5
)
3. Поиск с рангированием
from django.db.models import Q, F, Value
from django.db.models.functions import Length
# Поиск со скором релевантности
query = 'django tutorial'
posts = Post.objects.filter(
Q(title__icontains=query) |
Q(content__icontains=query)
).annotate(
relevance=Case(
When(title__icontains=query, then=Value(10)),
When(content__icontains=query, then=Value(1)),
output_field=IntegerField()
)
).order_by('-relevance')
Когда я использовал Raw SQL
# Рекурсивная иерархия (мой проект)
raw_sql = '''
WITH RECURSIVE category_tree AS (
SELECT id, name, parent_id FROM categories WHERE parent_id IS NULL
UNION ALL
SELECT c.id, c.name, c.parent_id FROM categories c
INNER JOIN category_tree ct ON c.parent_id = ct.id
)
SELECT * FROM category_tree
'''
categories = Category.objects.raw(raw_sql)
Заключение
- Django ORM покрывает 80% случаев отлично
- Для 20% нужен Raw SQL или знание ORM
- Я знаю оба пути
- Оптимизирую с select_related/prefetch_related
- Если ORM становится слишком сложным - пишу Raw SQL
- Всегда используй EXPLAIN ANALYZE для дебага
- Профилируй перед оптимизацией