簡體   English   中英

在 fastapi db session 中使用生成器上下文管理器

[英]Working with Generator Context Manager in fastapi db session

我在 fastapi session 中使用上下文管理器,我的設置如下:

from sqlmodel import create_engine, Session

from app.core.config import settings

engine = create_engine(settings.SQLALCHEMY_DATABASE_URI, pool_pre_ping=True)
SessionLocal = Session(autocommit=False, autoflush=False, bind=engine)

然后被消費

from contextlib import contextmanager
from sqlmodel import Session
from app.db.session import SessionLocal

@contextmanager
def get_session():
    with SessionLocal as session:
        yield session

這是端點使用的

@router.post("/")
def create_book(
    *,
    db: Session = Depends(get_session),
    book_in: models.BookCreate,
    current_user: models.User = Depends(get_current_active_user),
) -> models.BookCreate:
    """
    Create new book.
    """
    # print(current_user.id)
    book = crud.book.create_with_owner(db=db, obj_in=book_in, owner_id=current_user.id)
    return book

通過上面的設置,我希望一切正常,但事實並非如此; 相反,我收到以下錯誤:

backend-1  |   File "/app/app/api/api_v1/endpoints/book.py", line 42, in create_book
backend-1  |     book = crud.book.create_with_owner(db=db, obj_in=book_in, owner_id=current_user.id)
backend-1  |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
backend-1  |   File "/app/app/crud/crud_book.py", line 23, in create_with_owner
backend-1  |     db.add(db_obj)
backend-1  |     ^^^^^^
backend-1  | AttributeError: '_GeneratorContextManager' object has no attribute 'add'

以下是crud_book.py的詳細信息

class CRUDBook(CRUDBase[Book, BookCreate, BookUpdate]):
    def create_with_owner(
        self, db: Session, *, obj_in: BookCreate, owner_id: UUID
    ) -> Book:
        # with get_session() as db:
        obj_in_data = jsonable_encoder(obj_in)
        obj_in_data = dict(obj_in)

        print(db)
        # TODO: Check if owner_id is none

        db_obj = self.model(**obj_in_data, owner_id=owner_id)
        db.add(db_obj)
        db.commit()
        db.refresh(db_obj)
        return db_obj

當使用crud_book.py引入另一個上下文時,session 確實有效,但會帶來成本,因為 session 僅在 db 中創建書籍 object 時存在,但在出現以下錯誤后會在那里關閉

backend-1  |   File "/usr/local/lib/python3.11/site-packages/sqlmodel/main.py", line 597, in validate
backend-1  |     return cls.from_orm(value)
backend-1  |            ^^^^^^^^^^^^^^^^^^^
backend-1  |   File "/usr/local/lib/python3.11/site-packages/sqlmodel/main.py", line 552, in from_orm
backend-1  |     values, fields_set, validation_error = validate_model(cls, obj)
backend-1  |                                            ^^^^^^^^^^^^^^^^^^^^^^^^
backend-1  |   File "pydantic/main.py", line 1056, in pydantic.main.validate_model
backend-1  |   File "pydantic/utils.py", line 441, in pydantic.utils.GetterDict.get
backend-1  |   File "/usr/local/lib/python3.11/site-packages/sqlalchemy/orm/attributes.py", line 482, in __get__
backend-1  |     return self.impl.get(state, dict_)
backend-1  |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^
backend-1  |   File "/usr/local/lib/python3.11/site-packages/sqlalchemy/orm/attributes.py", line 942, in get
backend-1  |     value = self._fire_loader_callables(state, key, passive)
backend-1  |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
backend-1  |   File "/usr/local/lib/python3.11/site-packages/sqlalchemy/orm/attributes.py", line 978, in _fire_loader_callables
backend-1  |     return self.callable_(state, passive)
backend-1  |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
backend-1  |   File "/usr/local/lib/python3.11/site-packages/sqlalchemy/orm/strategies.py", line 863, in _load_for_state
backend-1  |     raise orm_exc.DetachedInstanceError(
backend-1  | sqlalchemy.orm.exc.DetachedInstanceError: Parent instance <Book at 0x7fc8a3087b90> is not bound to a Session; lazy load operation of attribute 'authors' cannot proceed (Background on this error at: https://sqlalche.me/e/14/bhk3)

為了以防萬一,以下是bookcreate期間預期的詳細信息

import datetime
from uuid import uuid4, UUID
from typing import TYPE_CHECKING

from sqlmodel import SQLModel, Field, Relationship

# if TYPE_CHECKING:
from .genre import Genre  # noqa: F401
from .author import Author  # noqa: F401
from .narrator import Narrator  # noqa: F401
from .publisher import Publisher  # noqa: F401
from .user import User, UserRead  # noqa: F401
from .book_genre_link import BookGenreLink  # noqa: F401
from .book_author_link import BookAuthorLink  # noqa: F401
from .book_narrator_link import BookNarratorLink  # noqa: F401
from .book_publisher_link import BookPublisherLink  # noqa: F401


class BookBase(SQLModel):
    title: str = Field(index=True)
    subtitle: str | None = Field(default=None, index=True)
    description: str = Field(index=True)
    runtime: datetime.time | None = Field(default=None)
    rating: float | None = Field(default=None)
    published_date: datetime.date | None = Field(default=None)
    cover_image: str | None = Field(default=None)

class Book(BookBase, table=True):
    id: UUID = Field(
        default_factory=uuid4,
        primary_key=True,
        index=True,
        nullable=False,
    )
    owner: list["User"] = Relationship(back_populates="books")
    owner_id: UUID | None = Field(default=None, foreign_key="user.id")
    authors: list["Author"] = Relationship(
        back_populates="books", link_model=BookAuthorLink
    )
    publishers: list["Publisher"] = Relationship(
        back_populates="books", link_model=BookPublisherLink
    )
    genres: list["Genre"] = Relationship(
        back_populates="books", link_model=BookGenreLink
    )
    narrators: list["Narrator"] = Relationship(
        back_populates="books", link_model=BookNarratorLink
    )
    # creation_date: datetime = Field(default=datetime.utcnow())
    # update_date: datetime = Field(default=datetime.utcnow())


class BookCreate(BookBase):
    authors: list["Author"] = []
    publishers: list["Publisher"] = []
    genres: list["Genre"] = []
    narrators: list["Narrator"] = []

為什么我需要兩個多上下文管理器來處理 session,或者我哪里出錯了?

出於某種原因,FastAPI 依賴項應該是生成器,而不是上下文管理器(在文檔中提到)。 我通常做的是

from contextlib import contextmanager

def get_db():
    # gaenerator using yield

db_context = contextmanager(get_db)

然后我可以在我的 FastAPI 端點中使用Depends(get_db) ,並在其他地方with db_context() as db:例如 Celery 任務、CRON 作業等)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM