Ответ
В Django ORM для обновления данных объекта модели существуют два основных подхода, различающихся производительностью и поведением.
1. Метод instance.save()
Этот способ предполагает сначала загрузку объекта из базы данных в память, изменение его полей, а затем сохранение обратно.
Как это работает:
- Выполняется SQL-запрос
SELECTдля получения объекта. - Атрибуты объекта изменяются в Python.
- Выполняется 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()
Это как прийти в гости, взять вазу со стола, выкинуть цветы, нассать туда, поставить обратно и ещё попрощаться. Много движений, зато всё по протоколу.
Как это работает, ёпта:
- Сначала ORM лезет в базу с криком «ДАЙ СЮДА ЭТОТ ОБЪЕКТ!» — это
SELECT. - Ты в Питоне эту штуку крутишь-вертишь, поля меняешь.
- Потом 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(). Иначе хеширование, блядь, не сработает, и будешь потом сидеть и думать, почему логин не проходит.