sqlalchemy.orm.exc.DetachedInstanceError: Instance is not bound to a Session при удалении пользователя

Постоянно выдает ошибку "sqlalchemy.orm.exc.DetachedInstanceError: Instance <User at 0x2d790022f70> is not bound to a Session; attribute refresh operation cannot proceed (Background on this error at: https://sqlalche.me/e/20/bhk3)". Не знаю как от нее избавиться, при этом пользователь не удаляется из БД. Прикладываю код, который может помочь разобраться.

@router1.message(Form.waiting_for_deletion_number)
async def process_delete_input(message: types.Message, state: FSMContext):
if message.text == 'Назад':
    await back(message, state)
    return
phone_number = message.text.strip()

if not is_valid_phone_number(phone_number):
    await message.answer("Номер телефона должен начинаться с '+'.")
    return

user = await rq.get_user_by_number(phone_number)
if user:
    tg_id = user.tg_id  
    fio = user.fio     
    user_deleted = await rq.delete_user_by_number(phone_number, tg_id, fio)
if user_deleted:
    await message.answer(f"Сотрудник с номером {phone_number} успешно удален.")
else:
    await message.answer(f"Сотрудник с номером {phone_number} не найден.")

await state.clear()  # Завершаем состояние после удаления

Этот код я использую как хэндлер-админ. Для удаления из БД прописан код следующий код:

async def get_user_by_number(number: str):
    async with async_session() as session:  
        async with session.begin(): 
            result = await session.execute(select(User).filter(User.number == number))
            return result.scalar_one_or_none() 

async def delete_user_by_number(number: str, tg_id: int, fio: str):
    async with async_session() as session:
        async with session.begin():
        # Извлекаем пользователя из базы данных
            user_to_delete = await session.scalar(
                select(User).where(User.tg_id == tg_id, User.number == number, User.fio == fio)
        )

        if user_to_delete:
            # Обновляем экземпляр (если он был отсоединен)
            await session.refresh(user_to_delete)
            await session.delete(user_to_delete)
            await session.commit()
            return True
        
        return False

Должно быть, чтобы в БД полностью удалялась строку по найденному номеру(т.е. все данные о пользователе)

DetachedInstanceError: Instance <User at 0x2d790022f70> is not bound to a Session; attribute refresh operation cannot proceed (Background on this error at: https://sqlalche.me/e/20/bhk3)
Traceback (most recent call last):
  File "C:\Users\Evilitself\OneDrive\Рабочий стол\Тг бот\.venv\lib\site-packages\aiogram\dispatcher\dispatcher.py", line 309, in _process_update
    response = await self.feed_update(bot, update, **kwargs)
  File "C:\Users\Evilitself\OneDrive\Рабочий стол\Тг бот\.venv\lib\site-packages\aiogram\dispatcher\dispatcher.py", line 158, in feed_update
    response = await self.update.wrap_outer_middleware(
  File "C:\Users\Evilitself\OneDrive\Рабочий стол\Тг бот\.venv\lib\site-packages\aiogram\dispatcher\middlewares\error.py", line 25, in __call__
    return await handler(event, data)
  File "C:\Users\Evilitself\OneDrive\Рабочий стол\Тг бот\.venv\lib\site-packages\aiogram\dispatcher\middlewares\user_context.py", line 56, in __call__
    return await handler(event, data)
  File "C:\Users\Evilitself\OneDrive\Рабочий стол\Тг бот\.venv\lib\site-packages\aiogram\fsm\middleware.py", line 42, in __call__
    return await handler(event, data)
  File "C:\Users\Evilitself\OneDrive\Рабочий стол\Тг бот\.venv\lib\site-packages\aiogram\dispatcher\event\telegram.py", line 121, in trigger
    return await wrapped_inner(event, kwargs)
  File "C:\Users\Evilitself\OneDrive\Рабочий стол\Тг бот\.venv\lib\site-packages\aiogram\dispatcher\event\handler.py", line 43, in call
    return await wrapped()
  File "C:\Users\Evilitself\OneDrive\Рабочий стол\Тг бот\.venv\lib\site-packages\aiogram\dispatcher\dispatcher.py", line 276, in _listen_update
    return await self.propagate_event(update_type=update_type, event=event, **kwargs)
  File "C:\Users\Evilitself\OneDrive\Рабочий стол\Тг бот\.venv\lib\site-packages\aiogram\dispatcher\router.py", line 146, in propagate_event
    return await observer.wrap_outer_middleware(_wrapped, event=event, data=kwargs)
  File "C:\Users\Evilitself\OneDrive\Рабочий стол\Тг бот\.venv\lib\site-packages\aiogram\dispatcher\router.py", line 141, in _wrapped
    return await self._propagate_event(
  File "C:\Users\Evilitself\OneDrive\Рабочий стол\Тг бот\.venv\lib\site-packages\aiogram\dispatcher\router.py", line 174, in _propagate_event
    response = await router.propagate_event(update_type=update_type, event=event, **kwargs)
  File "C:\Users\Evilitself\OneDrive\Рабочий стол\Тг бот\.venv\lib\site-packages\aiogram\dispatcher\router.py", line 146, in propagate_event
    return await observer.wrap_outer_middleware(_wrapped, event=event, data=kwargs)
  File "C:\Users\Evilitself\OneDrive\Рабочий стол\Тг бот\.venv\lib\site-packages\aiogram\dispatcher\router.py", line 141, in _wrapped
    return await self._propagate_event(
  File "C:\Users\Evilitself\OneDrive\Рабочий стол\Тг бот\.venv\lib\site-packages\aiogram\dispatcher\router.py", line 166, in _propagate_event
    response = await observer.trigger(event, **kwargs)
  File "C:\Users\Evilitself\OneDrive\Рабочий стол\Тг бот\.venv\lib\site-packages\aiogram\dispatcher\event\telegram.py", line 121, in trigger
    return await wrapped_inner(event, kwargs)
  File "C:\Users\Evilitself\OneDrive\Рабочий стол\Тг бот\.venv\lib\site-packages\aiogram\dispatcher\event\handler.py", line 43, in call
    return await wrapped()
  File "C:\Users\Evilitself\OneDrive\Рабочий стол\Тг бот\app\handler_admin.py", line 92, in process_delete_input
    tg_id = user.tg_id
  File "C:\Users\Evilitself\OneDrive\Рабочий стол\Тг бот\.venv\lib\site-packages\sqlalchemy\orm\attributes.py", line 566, in __get__
    return self.impl.get(state, dict_)  # type: ignore[no-any-return]
  File "C:\Users\Evilitself\OneDrive\Рабочий стол\Тг бот\.venv\lib\site-packages\sqlalchemy\orm\attributes.py", line 1086, in get
    value = self._fire_loader_callables(state, key, passive)
  File "C:\Users\Evilitself\OneDrive\Рабочий стол\Тг бот\.venv\lib\site-packages\sqlalchemy\orm\attributes.py", line 1116, in _fire_loader_callables
    return state._load_expired(state, passive)
  File "C:\Users\Evilitself\OneDrive\Рабочий стол\Тг бот\.venv\lib\site-packages\sqlalchemy\orm\state.py", line 803, in _load_expired
  File "C:\Users\Evilitself\OneDrive\Рабочий стол\Тг бот\.venv\lib\site-packages\sqlalchemy\orm\attributes.py", line 1116, in _fire_loader_callables
    return state._load_expired(state, passive)
  File "C:\Users\Evilitself\OneDrive\Рабочий стол\Тг бот\.venv\lib\site-packages\sqlalchemy\orm\attributes.py", line 1116, in _fire_loader_callables
    return state._load_expired(state, passive)
  File "C:\Users\Evilitself\OneDrive\Рабочий стол\Тг бот\.venv\lib\site-packages\sqlalchemy\orm\state.py", line 803, in _load_expired
    self.manager.expired_attribute_loader(self, toload, passive)
  File "C:\Users\Evilitself\OneDrive\Рабочий стол\Тг бот\.venv\lib\site-packages\sqlalchemy\orm\loading.py", line 1603, in load_scalar_attributes
    raise orm_exc.DetachedInstanceError(
sqlalchemy.orm.exc.DetachedInstanceError: Instance <User at 0x2d790022f70> is not bound to a Session; attribute refresh operation cannot proceed (Background on this error at: https://sqlalche.me/e/20/bhk3)

Ответы (1 шт):

Автор решения: CrazyElf

Ошибка происходит в этом коде:

user = await rq.get_user_by_number(phone_number)
if user:
    tg_id = user.tg_id  
    fio = user.fio

В этот момент сессия, которая была открыта в функции get_user_by_number уже закончилась и объект user уже не связан с БД. Вы можете отдавать из этой функции не ORM-объект, а только нужные вам поля:

user = await rq.get_user_by_number(phone_number)
if user:
    tg_id, fio = user

...

async def get_user_by_number(number: str):
    async with async_session() as session:  
        async with session.begin(): 
            result = await session.execute(select(User).filter(User.number == number))
            user = result.scalar_one_or_none()
            if user:
                return user.tg_id, user.fio
            else:
                return None

Код получился не очень красивый, но идея, я думаю, должна быть понятна.

→ Ссылка