путаница в синхронизации объектов SQLAlchemy с базой данных - PullRequest
0 голосов
/ 12 апреля 2019

Я прохожу учебник по SQLAlchemy ORM (https://docs.sqlalchemy.org/en/latest/orm/tutorial.html)), и мне очень трудно понять, когда / почему объект Python будет отражать последние данные в базе данных.

Вот последовательность событий, которая меня смущает:

  • Сначала мы создаем пользователя ed_user и добавляем его в сеанс. id равно None, поскольку строка не была записана в базу данных.
  • Затем мы создаем другого пользователя our_user, который получается запросом к базе данных запроса, который будет соответствовать ed_user. Так что our_user и ed_user на самом деле один и тот же пользователь. Когда мы выполняем запрос our_user.id или ed_user.id после выполнения этого запроса, мы видим, что id теперь назначен, потому что ed_user было сброшено в базу данных при написании запроса SELECT.
  • Затем мы редактируем ed_user, добавляем некоторые другие несвязанные строки и запускаем сессионный фикс.
  • Наконец, мы снова читаем значение ed_user.id, и это приводит к тому, что база данных выдает запрос SELECT, чтобы получить последнее значение id, так как предыдущий коммит завершил предыдущую транзакцию.

Я нахожу это крайне запутанным, потому что на первом шаге, до того, как ed_user когда-либо было записано в базу данных, SQLAlchemy согласился дать нам значение None для id , даже если бы он мог получить id, если он пошел вперед и сбросил запись в базу данных , но по какой-то причине, когда строка была записана в базу данных один раз, SQLAlchemy считает, что важно поддерживать ее актуальность (на последнем шаге) путем обновления данные, когда это читается. Почему это происходит и что контролирует это поведение?

В итоге, я понятия не имею, на какую логику я могу положиться в отношении того, когда / почему / как мои объекты Python будут обновляться с базой данных, и любая дополнительная ясность, которую вы можете предложить, будет чрезвычайно цениться.

1 Ответ

1 голос
/ 12 апреля 2019

Я попытаюсь пролить некоторый свет на управление состоянием в SQLAlchemy, пройдя ваши пункты маркера.

  • Сначала мы создадим пользователя ed_userи добавьте его в сессию.Его id равно None, поскольку строка не была записана в базу данных.

Перед добавлением вновь созданного объекта Ed в сеанс он находится в переходном процессе состояние;он не был добавлен в сеанс и не имеет идентификатора базы данных.Когда вы добавляете его в сеанс, он переходит в состояние в ожидании .Он не был сброшен в базу данных, но будет, когда произойдет следующая очистка.Если у вас включен autoflush (по умолчанию), все ожидающие изменения будут сброшены перед выполнением следующей операции запроса, чтобы убедиться, что состояния сеанса и базы данных синхронизируются при запросах, что приводит кнам:

  • Затем мы создаем другого пользователя our_user, который получается путем запроса к базе данных запроса, который будет соответствовать ed_user.Так что our_user и ed_user на самом деле один и тот же пользователь.

Сказать, что вы создаете our_user, немного ошибочно.Вместо этого вы выполняете запрос и привязываете результат к имени our_user:

>>> our_user = session.query(User).filter_by(name='ed').first()

Здесь важно помнить, что все ожидающие изменения сбрасываются до этот запрос имеет место.Это означает, что изменения, хранящиеся в объекте, связанном с именем ed_user, отправляются в базу данных, и SQLAlchemy извлекает его идентификатор базы данных (id больше не None), переводя его в состояние persistent и добавив его в карту идентификации .

Поскольку все, что произошло за до запроса, вы получите строку, которая была создана, когда объект Ed был очищен какВ результате и проверяя идентичность этой строки (используя карту идентификаторов) SQLAlchemy замечает, что она фактически представляет существующий объект, содержащийся в сеансе, тот, который был связан с именем ed_user ранее.Вот почему и ed_user.id, и our_user.id дают вам одинаковое значение - на самом деле ed_user is our_user также будет True;это один и тот же объект.

  • Наконец, мы снова читаем значение ed_user.id, и база данных выдает запрос SELECT, чтобы получить последнее значение idтак как предыдущий коммит завершил предыдущую транзакцию.

По умолчанию SQLAlchemy истекает все загруженное состояние базы данных после коммит , чтобы вы не могли работать с устаревшими данными.Некоторые другие потоки или процессы могли уже зафиксировать свои изменения между ними.Подобно большинству вещей, этим поведением можно управлять, передавая expire_on_commit=False в sessionmaker или Session напрямую, если вам действительно нужно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...