Какие существуют способы обновления объекта модели в Django ORM?

Ответ

В Django ORM для обновления данных объекта модели существуют два основных подхода, различающихся производительностью и поведением.

1. Метод instance.save()

Этот способ предполагает сначала загрузку объекта из базы данных в память, изменение его полей, а затем сохранение обратно.

Как это работает:

  1. Выполняется SQL-запрос SELECT для получения объекта.
  2. Атрибуты объекта изменяются в Python.
  3. Выполняется SQL-запрос UPDATE для сохранения изменений.

Пример:

from django.contrib.auth.models import User

# Требуется 2 запроса к БД (SELECT, затем UPDATE)
user = User.objects.get(id=1)
user.username = 'new_username'
user.email = 'new@example.com'
user.save()

Преимущества:

  • Вызывает сигналы pre_save и post_save.
  • Запускает пользовательскую логику, переопределенную в методе save().

2. Метод QuerySet.update()

Этот способ более эффективен, так как выполняет один прямой SQL-запрос UPDATE к базе данных без необходимости загружать объект в память.

Пример:

from django.contrib.auth.models import User

# Выполняется 1 запрос к БД (UPDATE)
updated_rows_count = User.objects.filter(id=1).update(username='new_username', email='new@example.com')

Важные ограничения:

  • Не вызывает сигналы pre_save и post_save.
  • Не вызывает переопределенный метод save() модели.
  • Не обновляет поле auto_now у DateTimeField.

Ключевые отличия и рекомендации

Критерий instance.save() QuerySet.update()
Производительность Ниже (минимум 2 запроса) Выше (1 запрос)
Вызов сигналов Да Нет
Вызов метода save() Да Нет
Массовое обновление Неэффективно Эффективно

Вывод:

  • Используйте QuerySet.update() для простых и массовых обновлений, где не требуется дополнительная логика (сигналы, кастомный save).
  • Используйте instance.save() когда необходимо, чтобы сработали сигналы или другая бизнес-логика, связанная с сохранением объекта.
  • Для смены пароля пользователя всегда используйте user.set_password('new_pass') и затем user.save(), чтобы обеспечить корректное хеширование.

Ответ 18+ 🔞

А, слушай, вот это тема, про которую я тебе сейчас так расскажу, что ты офигеешь, блядь. Два способа в Django данные обновлять, а разница между ними — как между поцелуем в щёчку и еблей в сраку на скорости. Всё зависит от того, насколько ты торопишься и нужна ли тебе вся эта побочная хуйня.

Первый способ — классика жанра, instance.save()

Это как прийти в гости, взять вазу со стола, выкинуть цветы, нассать туда, поставить обратно и ещё попрощаться. Много движений, зато всё по протоколу.

Как это работает, ёпта:

  1. Сначала ORM лезет в базу с криком «ДАЙ СЮДА ЭТОТ ОБЪЕКТ!» — это SELECT.
  2. Ты в Питоне эту штуку крутишь-вертишь, поля меняешь.
  3. Потом ORM несёт её обратно со словами «НА, ПЕРЕДЕЛАЛ!» — это UPDATE.
from django.contrib.auth.models import User

# Смотри, два запроса будет, нихуя не оптимизировано
user = User.objects.get(id=1)  # Раз! SELECT
user.username = 'new_username'
user.email = 'new@example.com'
user.save()  # Два! UPDATE

Чем хорош? А тем, что все твои хитрожопые сигналы pre_save и post_save отработают. И если ты в модели save() переопределил, чтобы там, не знаю, письмо админу отправлялось — то оно отправится. Всё чинно, благородно, но овердохуища неэффективно.

Второй способ — снайперский выстрел, QuerySet.update()

А это уже пришёл, пнул ногой вазу, она разбилась, и свалил. Быстро, чётко, но соседи не услышали.

from django.contrib.auth.models import User

# Один запрос, блядь! Прямой UPDATE в базу. Красота.
updated_rows_count = User.objects.filter(id=1).update(username='new_username', email='new@example.com')

Но тут, сука, подводные камни, как в болоте:

  • Сигналы? Забудь. Никаких pre_save/post_save. Тишина.
  • Твой кастомный save()? Не вызовется. Вообще.
  • Поля auto_now в DateTimeField? Не обновятся. Будут лежать, как мёртвые.

Так что же выбрать, ёбаный насос?

Смотри табличку, чтобы не ебать мозг:

Критерий instance.save() QuerySet.update()
Скорость Медленнее (2+ запроса) Быстрее (1 запрос)
Сигналы срабатывают? Да Нет, нихуя
Кастомный save() вызывается? Да Нет, опять нихуя
Массово обновить кучу записей? Полный пиздец, не делай так Идеально, одним махом

Короче, вывод простой, как три копейки:

  • Хочешь быстро и многоupdate(). Особенно если логика простая, «имя-емейл».
  • Хочешь, чтобы вся твоя умная обвязка отработалаsave().
  • И запомни нахуй, как «Отче наш»: пароль менять — только через user.set_password('new_pass') и потом user.save(). Иначе хеширование, блядь, не сработает, и будешь потом сидеть и думать, почему логин не проходит.