Изменили JSON-словарь, но UPDATE не появился: MutableDict
В модели настроек есть JSON-столбец. Обработчик делает settings.data[theme] = dark и вызывает commit. В памяти словарь изменён, ответ выглядит правильным, но новое соединение читает старую тему. ORM не выполнила UPDATE, потому что изменение внутреннего содержимого обычного dict не стало событием присваивания атрибуту.
Для отслеживания изменений верхнего уровня SQLAlchemy предоставляет MutableDict. Он сообщает ORM о мутациях словаря. Но вложенный обычный словарь автоматически не становится рекурсивно наблюдаемым: изменение data[ui][theme] требует отдельного подхода.
Как изменить решение
from copy import deepcopy
from sqlalchemy import JSON
from sqlalchemy.ext.mutable import MutableDict
from sqlalchemy.orm import Mapped, mapped_column
# Поле внутри существующего mapped-класса Settings:
data: Mapped[dict] = mapped_column(MutableDict.as_mutable(JSON))
# Изменение верхнего уровня отслеживается:
settings.data['theme'] = 'dark'
# Для вложенного изменения присваиваем новый документ:
updated = deepcopy(settings.data)
updated.setdefault('ui', {})['density'] = 'compact'
settings.data = updated
await session.commit()Первое изменение отправляет событие MutableDict. Во втором мы создаём отдельный документ, меняем вложенное поле и присваиваем весь JSON-атрибут заново. Глубокая копия нужна, чтобы не изменить вложенный объект исходного значения до присваивания.
Для больших документов полная копия может быть дорогой. Можно реализовать рекурсивно наблюдаемые контейнеры или выполнять адресное обновление JSON средствами базы, но тогда нужно явно продумать списки, вложенные словари и конкурентные изменения. Обычный MutableDict не обещает автоматическое отслеживание всего дерева.
Что проверить на своих данных
Проверяйте сохранение через новую сессию после commit. Чтение того же settings.data подтверждает только состояние Python-объекта. Включите SQL-лог и сравните верхнеуровневое изменение, вложенную мутацию без переприсваивания и исправленный вариант. Тест должен обнаруживать отсутствие UPDATE, а не только сравнивать словари в памяти.
Отдельная проблема — два клиента, одновременно меняющие разные части JSON. Перезапись всего документа из старого снимка может стереть соседнюю правку, даже если отслеживание мутации работает. Для такого сценария нужны согласованные обновления, блокировка или контроль версии. Поэтому сначала устраните отсутствие записи, а затем проверьте модель конкурентного доступа. Это два независимых свойства: ORM должна заметить изменение, а приложение должно корректно совместить несколько изменений.
Практика дальше
Сохранение JSON полезно проверять через API и новое чтение базы. На курсе FastAPI вы строите backend с SQLAlchemy, добавляете CRUD и пишете тесты pytest. Практика на SQLite даёт место закрепить такую проверку в полном сценарии запроса, а не только в манипуляции Python-объектом.
Посмотреть программу и практику курса
Источник: Изменили JSON-словарь, но UPDATE не появился — официальная документация.
Другие задачи по теме: База, HTTP-клиенты и тесты API.
Продолжить на практике
- FastAPI для начинающих: API с базой данных и тестами ↗
Соберёте свой первый полноценный backend на FastAPI: с базой данных, регистрацией, JWT-авторизацией и автотестами. 34 коротких урока и 140 заданий с проверкой — бесплатно.
