Как правильно использовать PonyORM с FastApi?

#python-asyncio #fastapi #ponyorm

#python-asyncio #fastapi #ponyorm

Вопрос:

Для личного проекта я использую PonyORM с FastApi; есть ли классный способ сохранить db_session на протяжении всего асинхронного вызова жизненного цикла конечной точки?

В документации PonyORM говорится об использовании декоратора и yield; но у меня это не сработало, поэтому, просмотрев другие проекты на Github, я нашел это обходное решение, которое работает нормально.

Но я действительно не знаю, что происходит за кулисами и почему документация Pony неточна в отношении темы асинхронности.

 def _enter_session():
    session = db_session(sql_debug=True)
    Request.pony_session = session
    session.__enter__()


def _exit_session():
    session = getattr(Request, 'pony_session', None)
    if session is not None:
        session.__exit__()


@app.middleware("http")
async def add_pony(request: Request, call_next):
    _enter_session()
    response = await call_next(request)
    _exit_session()
    return response
  

а затем в зависимости, например :

 async def current_user(
        username: str = Depends(current_user_from_token)) -> User:

    with Request.pony_session:
        # db actions 
  

и в вызове конечной точки :

 @router.post("/token", response_model=Token)
async def login_for_access_token(
        request: Request,
        user_agent: Optional[str] = Header(None),
        form_data: OAuth2PasswordRequestForm = Depends()):

    status: bool = authenticate_user(
        form_data.username,
        form_data.password,
        request.client.host,
        user_agent)


@db_session
def authenticate_user(
        username: str,
        password: str,
        client_ip: str = 'Undefined',
        client_app: str = 'Undefined'):
    user: User = User.get(email=username)
  

Если у вас, ребята, есть лучший способ или хорошее объяснение, я хотел бы услышать об этом 🙂

Комментарии:

1. Есть ли причина, по которой вы не можете следовать шаблонам, изложенным в документации для других orm? Т.Е. Просто использовать простую зависимость? Смотрите fastapi.tiangolo.com/tutorial/dependencies /…

2. причина в этом: github.com/tiangolo/fastapi/issues/891#issuecomment-612831548 и github.com/ponyorm/pony/issues/494

3. Там нет ничего, что, как я вижу, мешает вам использовать обычную зависимость

Ответ №1:

Я своего рода разработчик PonyORM и пользователь FastAPI.

Проблема с async и Pony заключается в том, что Pony использует транзакции, которые в нашем понимании являются атомарными. Также мы используем локальный кэш потока, который можно использовать в другом сеансе, если контекст переключится на другую сопрограмму. Я согласен, что мы должны добавить информацию об этом в документацию.

Чтобы быть уверенным, что все будет в порядке, вы должны использовать db_session в качестве диспетчера контекста и убедиться, что у вас нет асинхронных вызовов внутри этого блока кода.

Если ваши конечные точки не являются асинхронными, вы также можете использовать db_session для них декоратор.

В Pony мы согласны с тем, что использование ContextVar вместо Local должно помочь в некоторых случаях.

Ответ в одном предложении: используйте небольшие короткие сеансы и не прерывайте их асинхронностью.

Комментарии:

1. Спасибо за ваши ответы 🙂

2. еще один вопрос, вы находите мой подход хорошим? Я все еще получаю случайный HTTP 500 типа: File «/usr/local/lib/python3.8/site-packages/pony/orm/core.py «, строка 475, при выходе assert local.db_session является db_session AssertionError

3. Изменился ли ваш подход после моего ответа? Если да — как?

4. Еще раз привет. Нет, ничего не изменилось.

5. Итак, ваша проблема здесь — db_session декораторы, которые используют async inside. Вам нужно заменить его на db_session context manager

Ответ №2:

Попробуйте использовать стандартную зависимость fastapi:

 from fastapi import Depends

async def get_pony():
    with db_session(sql_debug=True) as session:
        yield session


async def current_user(
    username: str = Depends(current_user_from_token),
    pony_session = Depends(get_pony)) -> User:

    with pony_session:
        # db actions 
  

Комментарии:

1. спасибо за ответ 🙂 этот код не сработал для меня, я думаю, что буду придерживаться операций синхронизации и сохраню db_session в промежуточном программном обеспечении 🙂