Как при вызове метода модели save Django определяет вызывать sort или update?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Django save() — INSERT vs UPDATE
Методы save() в Django ORM — это универсальный способ сохранения моделей. Django должен решить: создавать новый объект (INSERT) или обновлять существующий (UPDATE)?
Как Django определяет операцию
Django использует следующую логику:
- Проверка первичного ключа (Primary Key)
- Если
pk(илиid) не установлен или None → INSERT - Если
pkустановлен и существует в БД → UPDATE - Если
pkустановлен, но НЕ существует в БД → INSERT
- Если
from django.db import models
class User(models.Model):
name = models.CharField(max_length=100)
email = models.EmailField()
# INSERT — pk не установлен
user1 = User(name="John", email="john@example.com")
user1.save() # INSERT INTO users ...
print(user1.id) # Теперь есть ID
# UPDATE — pk существует в БД
user2 = User.objects.get(id=1)
user2.name = "John Updated"
user2.save() # UPDATE users SET name = ...
# INSERT — pk установлен, но не существует
user3 = User(id=999, name="Jane", email="jane@example.com")
user3.save() # INSERT INTO users ...
Проверка существования в БД
Django проверяет наличие объекта в БД двумя способами:
Способ 1: Через атрибут _state.adding
user = User(name="Alice")
print(user._state.adding) # True — объект новый
user.save()
print(user._state.adding) # False — объект в БД
# После save() Django устанавливает _state.adding = False
Способ 2: Проверка primary key через _base_manager
user = User.objects.get(id=1)
print(user.pk) # 1
# Django проверит: существует ли User с pk=1 в БД?
# Да → UPDATE
# Но это дорого для каждого save()!
# Поэтому Django использует _state.adding вместо запроса
Параметр force_insert и force_update
Иногда нужно явно указать операцию:
user = User(id=999, name="Bob", email="bob@example.com")
# Гарантированно INSERT, даже если pk существует
user.save(force_insert=True)
# Гарантированно UPDATE, даже если pk не существует (ошибка)
user.save(force_update=True) # IntegrityError, если pk не в БД
# Можно комбинировать
user.save(update_fields=['name', 'email'])
Логика Django
def save(self, force_insert=False, force_update=False, update_fields=None):
if force_insert and force_update:
raise ValueError("Cannot force both insert and update")
# Определяем, нужен ли INSERT или UPDATE
if self.pk is None or force_insert:
# INSERT
self._do_insert(update_fields, force_insert=True)
elif force_update or self._state.adding is False:
# UPDATE
self._do_update(update_fields)
else:
# По умолчанию проверяем _state.adding
if self._state.adding:
self._do_insert(update_fields)
else:
self._do_update(update_fields)
Практические примеры
Создание нового объекта:
# Способ 1 — простой
user = User(name="Charlie", email="charlie@example.com")
user.save() # INSERT
# Способ 2 — через create()
user = User.objects.create(name="David", email="david@example.com")
# Способ 3 — через get_or_create()
user, created = User.objects.get_or_create(
email="eve@example.com",
defaults={"name": "Eve"}
)
if created:
print("Новый пользователь создан")
else:
print("Пользователь уже существует")
Обновление существующего:
# Получить из БД
user = User.objects.get(id=1)
# Изменить
user.name = "Updated Name"
# save() автоматически выполнит UPDATE
user.save()
# Или UPDATE с фильтром
User.objects.filter(id=1).update(name="Updated Name")
Проблема N+1 при обновлении:
# ❌ Плохо — много UPDATE запросов
users = User.objects.all()
for user in users:
user.name = user.name.upper()
user.save() # N запросов UPDATE
# ✅ Хорошо — один UPDATE запрос
User.objects.all().update(name=models.F('name').upper())
Когда Django может ошибиться
Проблема: pk установлен, но объект не в БД
# Это создаст INSERT, но pk уже установлен
user = User(id=100, name="Frank")
user.save() # INSERT с id=100
# Но если позже:
user.name = "Frank Updated"
user.save() # UPDATE? Или INSERT?
# Django выберет UPDATE (pk установлен, _state.adding=False)
Решение: использовать force_insert/force_update
# Если уверены, что это новый объект
user = User(id=100, name="Grace")
user.save(force_insert=True)
# Если уверены, что объект в БД
user = User(id=1, name="Helen")
user.save(force_update=True)
Проверка состояния объекта
user = User(name="Ivan")
# До save()
print(f"pk: {user.pk}") # None
print(f"_state.adding: {user._state.adding}") # True
user.save()
# После save()
print(f"pk: {user.pk}") # 1
print(f"_state.adding: {user._state.adding}") # False
Вывод
Django определяет INSERT vs UPDATE по простому правилу:
- primary key == None → INSERT
- primary key != None и _state.adding == False → UPDATE
- force_insert=True → всегда INSERT
- force_update=True → всегда UPDATE
Это очень эффективно и работает в 99% случаев. Но в сложных сценариях используй force_insert или force_update.