#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 в промежуточном программном обеспечении 🙂