SQLAlchemy — о проблемах с сессиями

Admin SQLAlchemy

SQLAlchemy, как и многие другие ОРМ работает с базой данных через сессии. Это отличается от работы на прямую с базой данных. Не понимания принципа работы сессий может приводить к надоедливым ошибкам.

Ошибки

sqlalchemy.orm.exc.DetachedInstanceError: Instance is not bound to a Session; attribute refresh operation cannot proceed (Background on this error at: http://sqlalche.me/e/13/bhk3)

«attribute refresh operation cannot proceed» % (state_str(state))
sqlalchemy.orm.exc.DetachedInstanceError: Instance is not bound to a Session; attribute refresh operation cannot proceed (Background on this error at: http://sqlalche.me/e/13/bhk3)

Как избежать ошибок на примере кода ниже, где все работает корректно:

def ploshadka_function(self):

    # Положим в переменную все ID пользователей (чтобы потом работать с другой сессией)
    user_ids = [x.id for x in db.session.query(User.id).distinct()]

    for x_id in user_ids:
        # Теперь мы можем работать с новой сессией
        user = db.session.query(User).filter_by(id=x_id).first()

        for setting in user.settings:
            row = vars(setting)

            if row['name'] == 'market_direction':
                self.send_market_direction(user)

            elif row['name'] == 'change_percent':
                self.send_change_percent(user, row['percent'])

            # Обновляем дату
            db.session.query(UserSetting).filter_by(name=row['name']).update({'key': value})

        # Коммитим все изменения после обработки одного пользователя
        db.session.commit()

В этом коде могло быть 2 ошибки в пересечениях сессий.

1.
Если бы мы использовали db.session.commit() внутри второго цикла for, то SQLAlchemy закрыл бы сессию, посчитав, что задача выполнена.

Вместо этого мы обновляем методом update наш экземпляр класса User, созданный выше. А в самом конце делаем commit.

2.
Но db.session.commit() все еще внутри первого цикла и мы не можем вынести его за него. Поэтому мы кладем наверху ID пользователей в переменную user_ids. ID собраны отдельно и при закрытии сессии нам не страшно, что закроется сеанс.

Например, такой код при взаимодействии выше, вызвал бы ошибки с сессиями:

users = db.session.query(User).all()
for user in users:
   settings = db.session.query(UserSetting).filter_by(user_id=user.id).all()

Это произошло бы из-за db.session.commit() внутри первого цикла for. После первого прохода в переменной users ничего бы не осталось. И во втором проходе возникла бы ошибка «Instance is not bound to a Session».

На сайте отсутствует реклама! Значете почему?

Помогать людям - моё хобби. А навыки разработчика позволяют не парится нулевой монетизизацией этого сайта. Хотя...

Если вам помогла информация, то даже от доната в 40 рублей мне будет приятно. Докину немного, куплю латте в макдаке, вспомню за чей счет банкет и карма вам зачтется!

Но и просто оставленный комментарий благодарности ниже принесет мне улыбку радости :)

А если захочется написать всякие гадости с переходом на личности, да тоже не стесняйтесь, но обычно я отправляю такое в спам. Люблю, когда дома чисто.

Добавить комментарий

Напишите свой комментарий, если вам есть что добавить/поправить/спросить по теме текущей статьи:
"SQLAlchemy — о проблемах с сессиями"