📖 Теория: Django ORM-запросы

Контекст практикума — всё необходимое перед задачами

⚡ Самое важное

  • Django shell: python manage.py shell — интерактивный Python с доступом к проекту
  • Создание: obj.save() или Model.objects.create()
  • Запросы: Model.objects.all(), .filter(), .exclude()
  • Q-класс: Q(field=val) & Q(...), Q(...) | Q(...), ~Q(...)
  • F-класс: ссылка на значение поля в БД без загрузки в Python
  • bulk_update: массовое обновление без N+1 запросов

← К оглавлению урока

Модели практикума

В практикуме используется приложение management_app с пятью моделями:

  • Tag — тэг задачи (name)
  • Project — проект (name, description, date_of_creation)
  • ProjectFile — файл проекта (file_name, file_path, created_at); M2M к Project через project_files
  • User — стандартная модель Django django.contrib.auth.models.User
  • Task — задача (name, status, priority, due_date, created_date, assignee→User, project→Project, tags M2M→Tag)

Django shell

Django shell — это интерактивная Python-консоль, в которой уже настроено окружение Django: можно сразу импортировать модели и делать ORM-запросы.

python manage.py shell

После запуска появляется стандартный Python REPL с доступом ко всем приложениям проекта.

QuerySet API — ключевые методы

Создание объектов

# Вариант 1: create + save
obj = MyModel(field='value')
obj.save()

# Вариант 2: objects.create (сразу сохраняет в БД)
obj = MyModel.objects.create(field='value')

Чтение

MyModel.objects.all()          # все записи
MyModel.objects.first()        # первая
MyModel.objects.last()         # последняя
MyModel.objects.count()        # количество
MyModel.objects.filter(k=v)    # фильтр по условию
MyModel.objects.exclude(k=v)   # исключить по условию
MyModel.objects.filter(k=v).exists()  # True/False

Lookups (поиск по полям)

Model.objects.filter(name__icontains='text')   # содержит (без учёта регистра)
Model.objects.filter(date__gt=value)            # строго больше
Model.objects.filter(date__gte=value)           # больше или равно
Model.objects.filter(date__lte=value)           # меньше или равно
Model.objects.filter(date__month=F('...') + 1) # по месяцу
Model.objects.filter(field__isnull=True)        # NULL
Model.objects.filter(related__name='value')     # через связь (JOIN)

Q-класс — комбинированные условия

Q-класс позволяет строить сложные условия с логическими операторами:

from django.db.models import Q

# AND
Model.objects.filter(Q(status='new') & Q(priority='Urgent'))

# OR
Model.objects.filter(Q(status='new') | Q(priority='Critical'))

# NOT
Model.objects.filter(~Q(tags__name='Q&A'))

# Комбинация
Model.objects.filter(
    (Q(status='new') & Q(priority='Urgent')) | ~Q(tags__name='Q&A')
)

F-класс — ссылка на значение поля

F-класс позволяет обращаться к значению поля на стороне БД — без загрузки объекта в Python. Это эффективно при массовых обновлениях.

from django.db.models import F
from datetime import timedelta

# Увеличить due_date на 7 дней для всех задач
Task.objects.update(due_date=F('due_date') + timedelta(weeks=1))

# Фильтрация: задачи, чей due_date приходится на следующий месяц после создания
Task.objects.filter(due_date__month=F('created_date__month') + 1)

Обновление

# Обновить через QuerySet (один SQL UPDATE)
Task.objects.filter(name='Update schema').update(status='pending')

# Массовое обновление через bulk_update
tasks = Task.objects.filter(status='new')
for task in tasks:
    task.status = 'in_progress'
Task.objects.bulk_update(tasks, ['status'])
bulk_update vs update()
.update() выполняет один SQL UPDATE с WHERE — быстро, но не вызывает сигналы и save().
bulk_update() вызывает один SQL-запрос по списку объектов — подходит когда значения разные для каждого объекта.

Агрегация и аннотация

from django.db.models import Count

# Аннотировать каждый проект количеством файлов
Project.objects.annotate(file_count=Count('project_files'))

# Фильтровать по аннотированному полю
Project.objects.annotate(
    file_count=Count('project_files')
).filter(file_count__gte=3)

ManyToMany — добавление связей

# Добавить тэг к задаче
task.tags.add(tag_object)

# Добавить файл к проекту
project.project_files.add(file_object)

← К оглавлению урока