Понимание символов _, __ и . в Django ORM
Сводка по user_id, user.id и user__id
В коде Django вы, вероятно, уже сталкивались с этими конструкциями.
Post.objects.filter(user_id=1)
Post.objects.filter(user__id=1)
post.user.id
post.user_id
Смешение подчеркивания, двойного подчеркивания и точки может сбивать с толку. Если начать использовать их произвольно, вы столкнетесь с тонкими различиями в поведении и производительности.
В этой статье мы разберём:
- Что именно означают
_,__и.в Django ORM - Различия в значении и производительности
user_id,user.idиuser__id - Когда и какой вариант лучше использовать в реальной работе
1. Общий обзор: роли трёх символов
Кратко:
_(один подчеркивание)- Это просто часть имени.
- Примеры:
created_at,user_id. -
В Django
ForeignKeyавтоматически создаёт поле<имя_поля>_idв базе. -
__(двойное подчеркивание) - Это разделитель запросов (lookup separator), специфичный для Django ORM.
- Используется только внутри имен аргументов
filter(),exclude(),order_by(),values()и т.д. -
Примеры:
age__gte=20(поле + условие)user__email='a@b.com'(доступ к полю через связь)created_at__date=...(преобразование + условие)
-
.(точка) - Оператор доступа к атрибуту в Python.
- Примеры:
obj.user,obj.user.id,qs.filter(...).order_by(...). - В ORM это просто обращение к атрибуту уже загруженного объекта; при необходимости может вызывать дополнительный запрос (ленивая загрузка).
2. _ и _id: значение вокруг ForeignKey
2.1 Пример определения модели
from django.contrib.auth import get_user_model
from django.db import models
User = get_user_model()
class Post(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
title = models.CharField(max_length=100)
В базе создаются поля:
postтаблицаid(PK)user_id(реальный FK‑поле)title
Django создаёт:
- атрибут
post.user– объектUser - атрибут
post.user_id– целое число (PK пользователя)
post.user # -> объект User (может вызвать дополнительный запрос)
post.user_id # -> PK пользователя (уже есть, запрос не нужен)
2.2 Использование _id в запросах
# 1) Передаём объект User
Post.objects.filter(user=request.user)
# 2) Передаём PK
Post.objects.filter(user=request.user.id)
# 3) Используем имя поля напрямую
Post.objects.filter(user_id=request.user.id)
Все три компилируются в один WHERE‑условие. _id – это имя реального столбца в БД.
3. __ – цепочка поиска в Django ORM
__ в Django – это разделитель запросов. Он позволяет:
- переходить по связям
- добавлять условия
- применять преобразования
3.1 Базовый шаблон
поле__условиесвязь__другое_полеполе__преобразование__условие
Примеры:
# 1) Простой фильтр
Post.objects.filter(title__icontains="django")
# 2) Через связь
Post.objects.filter(user__email__endswith="@example.com")
# 3) По дате
Post.objects.filter(created_at__year=2025)
При использовании user__email в БД выполняется JOIN.
4. . и user.id, user_id
4.1 Разница в экземплярах Django
post = Post.objects.first()
post.user # объект User (может вызвать запрос)
post.user.id # PK объекта User
post.user_id # уже загруженное значение FK
post.user– при первом обращении может вызвать дополнительный запрос.post.user_id– всегда доступно без запроса.
4.2 Пример производительности
# views.py
posts = Post.objects.all() # без select_related
for post in posts:
print(post.user.id) # 1 запрос + N запросов к User
Если posts содержит 100 записей, будет 101 запрос.
for post in posts:
print(post.user_id) # только 1 запрос
Использование select_related("user") позволяет обращаться к post.user без дополнительных запросов.
5. Сравнение user_id, user.id, user__id
5.1 user_id – прямой доступ к столбцу
- Где? В экземплярах модели и в запросах.
- Значение: реальный FK‑столбец.
- Производительность: без дополнительных запросов.
5.2 user.id – доступ через объект
- Где? Только в Python‑объектах.
- Значение: PK объекта User.
- Производительность: может вызвать дополнительный запрос, если не использован
select_related.
5.3 user__id – условие через связь
- Где? Только в аргументах запросов.
- Значение: условие на поле
idсвязанной модели. - Производительность: обычно приводит к
JOIN, но в простых случаях может быть оптимизировано.
6. Часто встречающиеся паттерны
# 1) Прямое поле FK
Post.objects.filter(user_id=1)
# 2) Объект FK
Post.objects.filter(user=1)
# 3) Через связь
Post.objects.filter(user__id=1)
Post.objects.filter(user__email__icontains="@example.com")
Рекомендации:
- Для простого фильтра по PK –
user_idилиuser=request.user. - Для фильтра по другим полям связанной модели –
user__email,user__is_active.
7. Практические правила
- Фильтрация по FK
python Post.objects.filter(user=request.user) Post.objects.filter(user_id=request.user.id) - Фильтрация по полям связанной модели
python Post.objects.filter(user__is_active=True) Post.objects.filter(user__email__endswith="@example.com") - Только PK в шаблонах
django {{ post.user_id }} {# без лишних запросов #} - Частый доступ к полям User
python posts = Post.objects.select_related("user") for post in posts: print(post.user.username, post.user.email) - Будьте внимательны с
__– каждый__может добавитьJOIN.
8. Итог
_– обычное имя, например<поле>_id– реальный FK‑столбец.__– разделитель запросов в Django ORM..– доступ к атрибуту Python‑объекта.user_id– значение FK в таблицеPost.user.id– PK объектаUser.user__id– условие на полеidсвязанной модели в запросе.
Точное понимание этих различий помогает писать более читаемый код, избегать лишних JOIN и проблем N+1.

Комментариев нет.