SQLAlchemy, получить объект, не связанный с сеансом
Я пытаюсь получить коллекцию объектов из базы данных и передать ее другому процессу, который не связан с базой данных. Мой код похож на приведенный ниже, но я продолжаю получать:
sqlalchemy.exc.UnboundExecutionError: Instance <MyClass at 0x8db7fec> is not bound to a Session; attribute refresh operation cannot proceed
Когда я пытаюсь посмотреть на элементы моего списка вне метода get_list()
.
def get_list (obj):
sesson = Session()
lst = session.query(MyClass).all()
session.close()
return lst
Однако, если я использую это:
def get_list_bis (obj)
session = Session()
return session.query(MyClass).all()
Я могу использовать элементы, но беспокоюсь о состоянии сеанса, поскольку он не был закрыт.
Что мне здесь не хватает?
Ответы
Ответ 1
Если вы хотите, чтобы куча объектов, созданных путем запроса сеанса, была полезной вне сферы действия сеанса, вам необходимо удалить их для сеанс.
В вашем первом примере функции вам нужно будет добавить строку:
session.expunge_all()
перед
session.close()
В более общем плане, скажем, сессия не закрыта сразу, как в первом примере. Возможно, это сеанс, который хранится в течение всей продолжительности веб-запроса или что-то в этом роде. В таких случаях вы не хотите делать expunge_all
. Вам нужно быть более хирургическим:
for item in lst:
session.expunge(item)
Ответ 2
Это часто происходит из-за того, что объекты находятся в состоянии expired
, срок действия объектов истекает, например, после фиксации, затем, когда такие объекты с истекшим сроком годности собираются использоваться, ORM пытается их refresh
, но это не может быть сделано, когда объекты отсоединены от сеанса (например, потому что тот сеанс был закрыт). Этим поведением можно управлять, создавая сеанс с параметром expire_on_commit=False
.
>>> from sqlalchemy import inspect
>>> insp = inspect(my_object)
>>> insp.expired
True # then it will be refreshed...
Ответ 3
В моем случае я также сохранял связанную сущность, и этот рецепт помог мне обновить все экземпляры внутри сеанса, используя тот факт, что сеанс является итеративным:
map(session.refresh, iter(session)) # call refresh() on every instance
Это крайне неэффективно, но работает. Должно быть хорошо для юнит-тестов.
Последнее замечание: в Python3 map()
является генератором и ничего не будет делать. Используйте реальные циклы списочных представлений