Задать вопрос
  • Как связать SQLAlchemy модели 1 к 1 по полю id?

    @alanforester Автор вопроса
    2024-12-29 17:51:04.399 | INFO     | logging:callHandlers:1736 - ROLLBACK
    INFO:     127.0.0.1:64696 - "GET /api/v1/articles/229109785 HTTP/1.1" 500 Internal Server Error
    2024-12-29 17:51:04.458 | ERROR    | logging:callHandlers:1736 - Exception in ASGI application
    
    Traceback (most recent call last):
    
      File "/Users/alan/Dev/MPinside_Backend/api/venv/bin/uvicorn", line 8, in <module>
        sys.exit(main())
      File "/Users/alan/Dev/MPinside_Backend/api/venv/lib/python3.13/site-packages/click/core.py", line 1157, in __call__
        return self.main(*args, **kwargs)
      File "/Users/alan/Dev/MPinside_Backend/api/venv/lib/python3.13/site-packages/click/core.py", line 1078, in main
        rv = self.invoke(ctx)
      File "/Users/alan/Dev/MPinside_Backend/api/venv/lib/python3.13/site-packages/click/core.py", line 1434, in invoke
        return ctx.invoke(self.callback, **ctx.params)
      File "/Users/alan/Dev/MPinside_Backend/api/venv/lib/python3.13/site-packages/click/core.py", line 783, in invoke
        return __callback(*args, **kwargs)
      File "/Users/alan/Dev/MPinside_Backend/api/venv/lib/python3.13/site-packages/uvicorn/main.py", line 410, in main
        run(
      File "/Users/alan/Dev/MPinside_Backend/api/venv/lib/python3.13/site-packages/uvicorn/main.py", line 577, in run
        server.run()
      File "/Users/alan/Dev/MPinside_Backend/api/venv/lib/python3.13/site-packages/uvicorn/server.py", line 65, in run
        return asyncio.run(self.serve(sockets=sockets))
    
      File "/opt/homebrew/Cellar/python@3.13/3.13.1/Frameworks/Python.framework/Versions/3.13/lib/python3.13/asyncio/runners.py", line 194, in run
        return runner.run(main)
    
      File "/opt/homebrew/Cellar/python@3.13/3.13.1/Frameworks/Python.framework/Versions/3.13/lib/python3.13/asyncio/runners.py", line 118, in run
        return self._loop.run_until_complete(task)
    
    > File "/Users/alan/Dev/MPinside_Backend/api/venv/lib/python3.13/site-packages/uvicorn/protocols/http/h11_impl.py", line 406, in run_asgi
        result = await app(  # type: ignore[func-returns-value]
      File "/Users/alan/Dev/MPinside_Backend/api/venv/lib/python3.13/site-packages/uvicorn/middleware/proxy_headers.py", line 70, in __call__
        return await self.app(scope, receive, send)
      File "/Users/alan/Dev/MPinside_Backend/api/venv/lib/python3.13/site-packages/fastapi/applications.py", line 1054, in __call__
        await super().__call__(scope, receive, send)
      File "/Users/alan/Dev/MPinside_Backend/api/venv/lib/python3.13/site-packages/starlette/applications.py", line 113, in __call__
        await self.middleware_stack(scope, receive, send)
      File "/Users/alan/Dev/MPinside_Backend/api/venv/lib/python3.13/site-packages/starlette/middleware/errors.py", line 187, in __call__
        raise exc
      File "/Users/alan/Dev/MPinside_Backend/api/venv/lib/python3.13/site-packages/starlette/middleware/errors.py", line 165, in __call__
        await self.app(scope, receive, _send)
      File "/Users/alan/Dev/MPinside_Backend/api/venv/lib/python3.13/site-packages/starlette/middleware/exceptions.py", line 62, in __call__
        await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
      File "/Users/alan/Dev/MPinside_Backend/api/venv/lib/python3.13/site-packages/starlette/_exception_handler.py", line 62, in wrapped_app
        raise exc
      File "/Users/alan/Dev/MPinside_Backend/api/venv/lib/python3.13/site-packages/starlette/_exception_handler.py", line 51, in wrapped_app
        await app(scope, receive, sender)
      File "/Users/alan/Dev/MPinside_Backend/api/venv/lib/python3.13/site-packages/starlette/routing.py", line 715, in __call__
        await self.middleware_stack(scope, receive, send)
      File "/Users/alan/Dev/MPinside_Backend/api/venv/lib/python3.13/site-packages/starlette/routing.py", line 735, in app
        await route.handle(scope, receive, send)
      File "/Users/alan/Dev/MPinside_Backend/api/venv/lib/python3.13/site-packages/starlette/routing.py", line 288, in handle
        await self.app(scope, receive, send)
      File "/Users/alan/Dev/MPinside_Backend/api/venv/lib/python3.13/site-packages/starlette/routing.py", line 76, in app
        await wrap_app_handling_exceptions(app, request)(scope, receive, send)
      File "/Users/alan/Dev/MPinside_Backend/api/venv/lib/python3.13/site-packages/starlette/_exception_handler.py", line 62, in wrapped_app
        raise exc
      File "/Users/alan/Dev/MPinside_Backend/api/venv/lib/python3.13/site-packages/starlette/_exception_handler.py", line 51, in wrapped_app
        await app(scope, receive, sender)
      File "/Users/alan/Dev/MPinside_Backend/api/venv/lib/python3.13/site-packages/starlette/routing.py", line 73, in app
        response = await f(request)
      File "/Users/alan/Dev/MPinside_Backend/api/venv/lib/python3.13/site-packages/fastapi/routing.py", line 301, in app
        raw_response = await run_endpoint_function(
      File "/Users/alan/Dev/MPinside_Backend/api/venv/lib/python3.13/site-packages/fastapi/routing.py", line 212, in run_endpoint_function
        return await dependant.call(**values)
    
      File "/Users/alan/Dev/MPinside_Backend/api/app/aricles/views.py", line 33, in get_article_detail
        article = await articles_service.get_article_by_id(article_id)
    
      File "/Users/alan/Dev/MPinside_Backend/api/app/core/articles/services.py", line 11, in get_article_by_id
        return await self.repository.get_article(id=article_id)
    
      File "/Users/alan/Dev/MPinside_Backend/api/app/core/articles/repository.py", line 41, in get_article
        product = model.product
    
      File "/Users/alan/Dev/MPinside_Backend/api/venv/lib/python3.13/site-packages/sqlalchemy/orm/attributes.py", line 566, in __get__
        return self.impl.get(state, dict_)  # type: ignore[no-any-return]
      File "/Users/alan/Dev/MPinside_Backend/api/venv/lib/python3.13/site-packages/sqlalchemy/orm/attributes.py", line 1086, in get
        value = self._fire_loader_callables(state, key, passive)
      File "/Users/alan/Dev/MPinside_Backend/api/venv/lib/python3.13/site-packages/sqlalchemy/orm/attributes.py", line 1121, in _fire_loader_callables
        return self.callable_(state, passive)
      File "/Users/alan/Dev/MPinside_Backend/api/venv/lib/python3.13/site-packages/sqlalchemy/orm/strategies.py", line 967, in _load_for_state
        return self._emit_lazyload(
      File "/Users/alan/Dev/MPinside_Backend/api/venv/lib/python3.13/site-packages/sqlalchemy/orm/strategies.py", line 1068, in _emit_lazyload
        return loading.load_on_pk_identity(
      File "/Users/alan/Dev/MPinside_Backend/api/venv/lib/python3.13/site-packages/sqlalchemy/orm/loading.py", line 694, in load_on_pk_identity
        session.execute(
      File "/Users/alan/Dev/MPinside_Backend/api/venv/lib/python3.13/site-packages/sqlalchemy/orm/session.py", line 2362, in execute
        return self._execute_internal(
      File "/Users/alan/Dev/MPinside_Backend/api/venv/lib/python3.13/site-packages/sqlalchemy/orm/session.py", line 2247, in _execute_internal
        result: Result[Any] = compile_state_cls.orm_execute_statement(
      File "/Users/alan/Dev/MPinside_Backend/api/venv/lib/python3.13/site-packages/sqlalchemy/orm/context.py", line 305, in orm_execute_statement
        result = conn.execute(
      File "/Users/alan/Dev/MPinside_Backend/api/venv/lib/python3.13/site-packages/sqlalchemy/engine/base.py", line 1418, in execute
        return meth(
      File "/Users/alan/Dev/MPinside_Backend/api/venv/lib/python3.13/site-packages/sqlalchemy/sql/elements.py", line 515, in _execute_on_connection
        return connection._execute_clauseelement(
      File "/Users/alan/Dev/MPinside_Backend/api/venv/lib/python3.13/site-packages/sqlalchemy/engine/base.py", line 1640, in _execute_clauseelement
        ret = self._execute_context(
      File "/Users/alan/Dev/MPinside_Backend/api/venv/lib/python3.13/site-packages/sqlalchemy/engine/base.py", line 1846, in _execute_context
        return self._exec_single_context(
      File "/Users/alan/Dev/MPinside_Backend/api/venv/lib/python3.13/site-packages/sqlalchemy/engine/base.py", line 1986, in _exec_single_context
        self._handle_dbapi_exception(
      File "/Users/alan/Dev/MPinside_Backend/api/venv/lib/python3.13/site-packages/sqlalchemy/engine/base.py", line 2358, in _handle_dbapi_exception
        raise exc_info[1].with_traceback(exc_info[2])
      File "/Users/alan/Dev/MPinside_Backend/api/venv/lib/python3.13/site-packages/sqlalchemy/engine/base.py", line 1967, in _exec_single_context
        self.dialect.do_execute(
      File "/Users/alan/Dev/MPinside_Backend/api/venv/lib/python3.13/site-packages/sqlalchemy/engine/default.py", line 941, in do_execute
        cursor.execute(statement, parameters)
      File "/Users/alan/Dev/MPinside_Backend/api/venv/lib/python3.13/site-packages/sqlalchemy/dialects/postgresql/asyncpg.py", line 572, in execute
        self._adapt_connection.await_(
      File "/Users/alan/Dev/MPinside_Backend/api/venv/lib/python3.13/site-packages/sqlalchemy/util/_concurrency_py3k.py", line 123, in await_only
        raise exc.MissingGreenlet(
    
    sqlalchemy.exc.MissingGreenlet: greenlet_spawn has not been called; can't call await_only() here. Was IO attempted in an unexpected place? (Background on this error at: https://sqlalche.me/e/20/xd2s)
    Написано
  • Как связать SQLAlchemy модели 1 к 1 по полю id?

    @alanforester Автор вопроса
    import logging
    import typing
    from asyncio import current_task
    from contextlib import asynccontextmanager
    from dataclasses import dataclass
    from typing import Any, AsyncGenerator
    
    from fastapi_utils.camelcase import camel2snake
    from sqlalchemy import inspect, literal, orm, select
    from sqlalchemy.ext.asyncio import (
        AsyncSession,
        async_scoped_session,
        create_async_engine,
    )
    from sqlalchemy.ext.declarative import declarative_base
    from sqlalchemy.orm import Session, declared_attr
    
    logger = logging.getLogger(__name__)
    
    
    class Base:
        id: typing.Any
        __name__: str
    
        @classmethod
        def get_real_column_name(cls, attr_name: str) -> str:
            return getattr(inspect(cls).c, attr_name).name
    
        @declared_attr
        def __tablename__(cls) -> str:
            return camel2snake(cls.__name__)
    
    
    @dataclass
    class Database:
        CONNECT_KWARGS = {
            "max_overflow": 10,
            "pool_pre_ping": True,
            "pool_recycle": 3600,
            "echo_pool": True,
        }
    
        def __init__(
            self,
            db_connect_url: str,
            db_alias: str,
            connect_kwargs: dict[str, Any],
            debug: bool = True,
        ) -> None:
            self._engine = create_async_engine(url=db_connect_url, **connect_kwargs, echo=debug)
            self._db_alias = db_alias
            self._async_session = async_scoped_session(
                orm.sessionmaker(
                    autocommit=False,
                    autoflush=False,
                    class_=AsyncSession,
                    expire_on_commit=False,
                    bind=self._engine,
                ),
                scopefunc=current_task,
            )
    
        async def create_tables_by_base(self, sqlalchemy_base: declarative_base) -> None:
            async with self._engine.begin() as conn:
                await conn.run_sync(sqlalchemy_base.metadata.create_all)
    
        async def get_status(self) -> dict[str, str]:
            async with self.session() as session:
                db_status = await session.execute(
                    select(
                        [
                            literal("ready").label("status"),
                            literal(self._db_alias).label("name"),
                        ],
                    ),
                )
                return db_status.first()._asdict()
    
        @asynccontextmanager
        async def session(self) -> AsyncGenerator[Session, None]:
            session: Session = self._async_session()
            try:
                yield session
            except Exception:
                logger.exception("Session rollback because of exception")
                await session.rollback()
                raise
            finally:
                await session.close()
                await self._async_session.remove()
    
        async def disconnect(self) -> None:
            await self._engine.dispose()
    Написано