Как Django понимает, какие данные нужно получить?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Как Django понимает, какие данные нужно получить
Django использует ORM (Object-Relational Mapping) для преобразования Python кода в SQL запросы. Процесс довольно интеллектуален, и разберусь, как это работает под капотом.
Базовый механизм: QuerySet
Всё начинается с QuerySet — ленивого объекта, который описывает запрос, но не выполняет его сразу:
# Django НЕ выполняет запрос здесь
query = User.objects.filter(age__gte=18)
print(type(query)) # <class 'django.db.models.query.QuerySet'>
# Запрос выполняется только когда мы его "материализуем"
users = list(query) # СЕЙЧАС выполняется SQL
for user in query: # ИЛИ здесь
print(user)
user = query.first() # ИЛИ здесь
Как Django строит SQL
Django преобразует вызовы методов в SQL WHERE условия:
# Этот Python код:
User.objects.filter(
age__gte=18,
country="USA",
is_active=True
).exclude(
username__startswith="test_"
).order_by("-created_at")
# Превращается в SQL:
SELECT * FROM users
WHERE age >= 18
AND country = 'USA'
AND is_active = true
AND username NOT LIKE 'test_%'
ORDER BY created_at DESC
Джанго анализирует имена параметров с двойным подчёркиванием (__):
# Filter lookups
User.objects.filter(age__gte=18) # age >= 18
User.objects.filter(name__icontains="john") # name ILIKE '%john%'
User.objects.filter(created__year=2024) # YEAR(created) = 2024
User.objects.filter(email__isnull=True) # email IS NULL
# Relationships
User.objects.filter(posts__title__contains="Django") # JOIN posts
User.objects.filter(profile__age__gte=18) # JOIN profile
Парсинг query expressions
Джанго разбирает эти выражения на части:
class User(models.Model):
name = models.CharField(max_length=100)
age = models.IntegerField()
created_at = models.DateTimeField(auto_now_add=True)
# Django видит: "age__gte=18"
# Разбирает как:
# - model field: "age" -> IntegerField
# - lookup type: "gte" -> ">=" в SQL
# - value: 18
print(User._meta.get_field('age')) # <DjangoField: IntegerField>
Внутренняя структура:
from django.db.models import Q
# Внутри Django создаёт граф условий
query_condition = Q(age__gte=18) & Q(country="USA") | Q(is_vip=True)
# Это преобразуется в WHERE clause:
# (age >= 18 AND country = 'USA') OR is_vip = true
JOIN'ы для связанных моделей
Когда фильтруем по связанной таблице, Django автоматически добавляет JOIN:
class User(models.Model):
name = models.CharField(max_length=100)
class Post(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
title = models.CharField(max_length=200)
# Django видит это:
User.objects.filter(posts__title__contains="Django")
# И генерирует SQL с JOIN:
SELECT DISTINCT users.*
FROM users
INNER JOIN posts ON (users.id = posts.user_id)
WHERE posts.title LIKE '%Django%'
Джанго интеллектуально понимает отношения через ForeignKey, ManyToMany, OneToOne:
class Author(models.Model):
name = models.CharField(max_length=100)
class Post(models.Model):
author = models.ForeignKey(Author, on_delete=models.CASCADE)
title = models.CharField(max_length=200)
class Tag(models.Model):
posts = models.ManyToManyField(Post)
# Django понимает эти связи и строит нужные JOIN'ы
Author.objects.filter(post__title="Hello") # INNER JOIN posts
Post.objects.filter(tag__name="python") # INNER JOIN tags
Post.objects.filter(author__name="John") # INNER JOIN authors
select_related и prefetch_related
Джанго может выполнить неопределённо много запросов (N+1 problem):
# ❌ Плохо — N+1 запросов
for post in Post.objects.all():
print(post.author.name) # Доп. запрос для каждого автора!
# 1 запрос на посты + N запросов на авторов = N+1
# ✅ Хорошо — 2 запроса
for post in Post.objects.select_related('author'): # 1 JOIN
print(post.author.name) # Данные уже в памяти
# ✅ Для ManyToMany
for post in Post.objects.prefetch_related('tags'):
for tag in post.tags.all(): # Уже загружено
print(tag.name)
Внутри Django:
# select_related использует JOIN
SELECT posts.*, authors.* FROM posts
LEFT OUTER JOIN authors ON posts.author_id = authors.id
# prefetch_related использует IN clause
SELECT * FROM posts WHERE id IN (1, 2, 3, ...)
SELECT * FROM tags WHERE posts_id IN (1, 2, 3, ...)
SQL compilation
Джанго компилирует QuerySet в SQL через compiler:
from django.db.models.sql import compiler
query = User.objects.filter(age__gte=18)
# Получить SQL
sql, params = query.query.get_compiler(query.db).as_sql()
print(sql) # SELECT * FROM users WHERE age >= %s
print(params) # [18]
Aggregation и annotation
Джанго может вычислять агрегаты прямо в SQL:
from django.db.models import Count, Avg, Q
# Генерирует GROUP BY
User.objects.annotate(
post_count=Count('posts'),
avg_rating=Avg('posts__rating')
).filter(post_count__gte=5)
# SQL:
SELECT users.*,
COUNT(posts.id) as post_count,
AVG(posts.rating) as avg_rating
FROM users
LEFT JOIN posts ON users.id = posts.user_id
GROUP BY users.id
HAVING COUNT(posts.id) >= 5
Raw SQL fallback
Джанго может выполнить raw SQL, если ORM недостаточно:
# Когда ORM не справляется
users = User.objects.raw("""
SELECT u.*, COUNT(p.id) as post_count
FROM users u
LEFT JOIN posts p ON u.id = p.user_id
GROUP BY u.id
HAVING COUNT(p.id) > 5
""")
# Или прямой запрос
from django.db import connection
cursor = connection.cursor()
cursor.execute("SELECT * FROM users WHERE age > %s", [18])
rows = cursor.fetchall()
Оптимизация query
Джанго может анализировать запросы и предлагать оптимизации:
# Debug mode показывает SQL запросы
DEBUG = True
from django.db import connection
from django.test.utils import override_settings
# Смотреть SQL
for query in connection.queries:
print(query['sql'])
print(query['time'])
Query expression objects
Джанго использует специальные объекты для представления выражений:
from django.db.models import Q, F, Value, Case, When
# F — обращение к полю
User.objects.filter(age__gte=F('min_age'))
# Case/When — условная логика
User.objects.annotate(
status=Case(
When(age__lt=18, then=Value('minor')),
When(age__gte=18, then=Value('adult')),
default=Value('unknown')
)
)
# Q — логические операции
User.objects.filter(
Q(first_name__startswith='John') | Q(last_name__startswith='Smith')
)
Выводы
Джанго ORM работает так:
- Парсирует Python выражения — разбирает фильтры на части
- Анализирует модели — понимает типы полей и связи
- Строит SQL AST — создаёт абстрактное дерево синтаксиса
- Компилирует в SQL — конкретный диалект БД
- Выполняет с параметризацией — безопасно от SQL injection
- Кеширует результаты — select_related, prefetch_related
Это делает Django ORM мощным инструментом для безопасного и эффективного доступа к данным.