Слишком много вопросов в одном, поэтому те ответы, которые на все ответы не соответствуют формату ответов StackOverflow. Я постараюсь кратко описать подсказки, поэтому задайте им отдельный вопрос, если этого недостаточно.
Назначение пользователя и описания транзакции
Самый популярный способ сделать это - назначить пользователя (и другую информацию) какому-либо глобальному объекту (threading.local()
в многопоточном приложении). Это очень плохой способ, из-за которого трудно обнаружить ошибки.
Лучший способ - назначить пользователя сеансу. Это нормально, когда сессия создается для каждого веб-запроса (на самом деле, это лучший вариант для приложения с аутентификацией в любом случае), поскольку этот сеанс есть только у одного пользователя. Но прохождение описания таким способом не так хорошо.
И мое любимое решение состоит в том, чтобы расширить Session.commit()
метод для принятия необязательного пользовательского (и, возможно, другой информации) параметра и присвоения ему текущей транзакции. Это самое гибкое решение, и оно хорошо подходит для прохождения описания. Обратите внимание, что информация привязана к одной транзакции и передается очевидным образом, когда транзакция закрыта.
Обнаружение изменений
Там есть sqlalchemy.org.attributes.instance_state(obj)
содержит всю необходимую вам информацию. Наиболее полезным для вас является словарь state.committed_state
, который содержит исходное состояние для измененных полей (включая отношения «многие ко многим!»). Существует также метод state.get_history()
(или функция sqlalchemy.org.attributes.get_history()
), возвращающий объект истории с методом has_changes()
и свойствами added
и deleted
для нового и старого значения соответственно. В дальнейшем используйте state.manager.keys()
(или state.manager.attributes
) для получения списка всех полей.
Автоматическое сохранение изменений
SQLAlchemy поддерживает расширение mapper, которое может предоставлять хуки до и после обновления, вставки и удаления. Вы должны предоставить свое собственное расширение со всеми хуками до (вы не можете использовать после, так как состояние объектов изменяется при сбросе). Для декларативного расширения легко написать подкласс DeclarativeMeta
, который добавляет расширение mapper для всех ваших моделей. Обратите внимание, что вы должны сбрасывать изменения дважды, если вы используете сопоставленные объекты для журнала, поскольку единица работы не учитывает объекты, созданные в хуках.