← Назад к вопросам

Как при вызове метода модели save Django определяет вызывать sort или update?

1.0 Junior🔥 231 комментариев
#Django#Python Core

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI28 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Django save() — INSERT vs UPDATE

Методы save() в Django ORM — это универсальный способ сохранения моделей. Django должен решить: создавать новый объект (INSERT) или обновлять существующий (UPDATE)?

Как Django определяет операцию

Django использует следующую логику:

  1. Проверка первичного ключа (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 по простому правилу:

  1. primary key == None → INSERT
  2. primary key != None и _state.adding == False → UPDATE
  3. force_insert=True → всегда INSERT
  4. force_update=True → всегда UPDATE

Это очень эффективно и работает в 99% случаев. Но в сложных сценариях используй force_insert или force_update.