FastAPI如何访问接受用户输入并返回表的PostgreSQL自定义函数

yruzcnhs  于 2023-06-22  发布在  PostgreSQL
关注(0)|答案(1)|浏览(251)

我收到一个错误
422 Unprocessable Entity' '{"detail": [{"loc": ["body"], "msg": "field required", "type": "value_error.missing"}]}

from typing import Optional, List, Any, Generator
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy import create_engine, Column, Integer, String, Numeric, Enum
from sqlalchemy.sql import text, select, func, column
from sqlalchemy.orm import Session, as_declarative, sessionmaker
from sqlalchemy.ext.declarative import declared_attr
from pydantic import BaseModel, condecimal

from enums import Colour
from config import settings

SQLALCHEMY_DATABASE_URL = settings.DATABASE_URL
engine = create_engine(SQLALCHEMY_DATABASE_URL)

SessionLocal = sessionmaker(autocommit=False,
                            autoflush=False,
                            bind=engine)

def get_db() -> Generator:
    try:
        db = SessionLocal()
        yield db
    finally:
        db.close()

@as_declarative()
class Base:
    id: Any
    __name__: str

    @declared_attr
    def __tablename__(cls) -> str:
        return cls.__name__.lower()

class Pct(Base):
    pid = Column(Integer, primary_key= True, nullable= False)
    nm = Column(String, nullable= False)
    p = Column(Numeric(70,67), nullable= False)
    yr = Column(Integer, nullable= False)
    color = Column(Enum(Colour), default=Colour.unknown)
    kind = Column(String, nullable= False)

class PctBase(BaseModel):
    pid: int
    nm: str
    p: condecimal(max_digits=70, decimal_places=67)
    yr: int
    color: Optional[Colour]
    kind: str

def retrieve_pct(id: int, pct: PctBase, db: Session):
    pct_object = Pct(**pct.dict())
    pct_stmt = text('SELECT * FROM ptigree(:pid)', {'pid': id})
    db.execute(pct_stmt)
    db.commit()
    return pct_object

router = APIRouter()

@router.get("/get/{id}", response_model= List[PctBase])
def read_pct(id: int, pct: PctBase, db: Session = Depends(get_db)):
    pct = retrieve_pct(pct= pct, db= db, id= id)
    return pct

我想发送的SQL函数ptigreeid由我的用户选择。ptigree生成结果集(多行表,其中列:pidnmpyrcolorkind),我希望API端点向我的用户显示这些结果。
我能够得到它的工作按照下面给出的建议。更改涉及1)使pct_recieve使用SqlAlchemy获取实际结果:

def retrieve_pct(id: int, db: Session):
    pct_func = func.ptigree(id).table_valued(
        column("pid", Integer),
        column("horse_nm", String),
        column("p", Numeric(70,67)),
        column("crop", Integer),
        column("color", Enum(Colour)),
        column("sex", String))
    pct_stmt = select(pct_func)
    pct_result = db.execute(pct_stmt)
    db.commit
    return pct_result

处理结果,Pydantic在read_pct中逐行读取表:

@router.get("/get/{id}", response_model= List[PctBase])
def read_pct(id: int, db: Session = Depends(get_db)):
    pct_result = retrieve_pct(id= id, db= db)
    pct_objects = [PctBase(**row._asdict()) for row in pct_result]
    return pct_objects

在PgAdmin中,我可以通过使用以下语句来验证SQL函数是否有效:

SELECT * FROM ptigree(some_integer);

它将返回预期的具有不同行数的五列表。
我花了一段时间查看文档和其他问题,但还没有成功地理解如何解决这个问题:
FastAPI如何访问接受用户输入并返回表的PostgreSQL自定义函数?

vyswwuz2

vyswwuz21#

在FastAPI代码中,您似乎试图创建一个API端点,该端点通过用户输入调用PostgreSQL自定义函数(ptigree),并将结果作为表返回。但是,在尝试检索结果时遇到错误。
要访问在FastAPI中返回表的PostgreSQL自定义函数,可以使用SQLAlchemy的funcselect函数。下面是应该可以工作的代码的更新版本:

from typing import Optional, List, Any, Generator
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy import create_engine, Column, Integer, String, Numeric, Enum
from sqlalchemy.sql import text, select, func, column
from sqlalchemy.orm import Session, as_declarative, sessionmaker
from sqlalchemy.ext.declarative import declared_attr
from pydantic import BaseModel, condecimal

from enums import Colour
from config import settings

SQLALCHEMY_DATABASE_URL = settings.DATABASE_URL
engine = create_engine(SQLALCHEMY_DATABASE_URL)

SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)

def get_db() -> Generator:
    try:
        db = SessionLocal()
        yield db
    finally:
        db.close()

@as_declarative()
class Base:
    id: Any
    __name__: str

    @declared_attr
    def __tablename__(cls) -> str:
        return cls.__name__.lower()

class Pct(Base):
    pid = Column(Integer, primary_key=True, nullable=False)
    nm = Column(String, nullable=False)
    p = Column(Numeric(70, 67), nullable=False)
    yr = Column(Integer, nullable=False)
    color = Column(Enum(Colour), default=Colour.unknown)
    kind = Column(String, nullable=False)

class PctBase(BaseModel):
    pid: int
    nm: str
    p: condecimal(max_digits=70, decimal_places=67)
    yr: int
    color: Optional[Colour]
    kind: str

def retrieve_pct(id: int, db: Session):
    pct_func = func.ptigree(id).table_valued(
        column("pid", Integer),
        column("nm", String),
        column("p", Numeric(70, 67)),
        column("yr", Integer),
        column("color", Enum(Colour)),
        column("kind", String)
    )
    pct_stmt = select(pct_func)
    pct_result = db.execute(pct_stmt).fetchall()
    db.commit()
    return pct_result

router = APIRouter()

@router.get("/get/{id}", response_model=List[PctBase])
def read_pct(id: int, db: Session = Depends(get_db)):
    pct_result = retrieve_pct(id=id, db=db)
    pct_objects = [PctBase(**row._asdict()) for row in pct_result]
    return pct_objects

在此更新的代码中,retrieve_pct函数使用SQLAlchemy的func.ptigree调用PostgreSQL自定义函数,并使用table_valued指示它返回一个表。然后使用select(pct_func)创建SQLAlchemy select语句,并使用db.execute(pct_stmt).fetchall()获取所有结果。最后,它将获取的行转换为PctBase对象,并从API端点返回它们。
确保您有必要的导入(enumsconfig),并且在config.py文件中正确设置了数据库配置(settings.DATABASE_URL)。
请注意,您可能需要根据特定的数据库设置和Colour枚举的定义来调整代码。

相关问题