【发布时间】:2023-02-04 09:56:32
【问题描述】:
我正在使用堆栈 FastAPI、Pydantic 和 SQL Alchemy 编写 API,我遇到过很多情况,我必须查询数据库才能对有效负载值执行验证。让我们考虑一个示例 API,/forgot-password。此 API 将在有效负载中接受 email,我需要验证数据库中是否存在该电子邮件。如果电子邮件存在于数据库中,则将执行创建令牌和发送邮件等必要操作,否则 Pydantic 应针对该字段引发错误响应。错误响应必须是标准的PydanticValueError响应。这是因为所有验证错误都会有一致的响应,因为它变得易于为消费者处理。
有效载荷 -
{
"email": "example@gmail.com"
}
在 Pydantic 中,此模式和电子邮件验证实现为 -
class ForgotPasswordRequestSchema(BaseModel):
email: EmailStr
@validator("email")
def validate_email(cls, v):
# this is the db query I want to perform but
# I do not have access to the active session of this request.
user = session.get(Users, email=v)
if not user:
raise ValueError("Email does not exist in the database.")
return v
现在,如果我们像这样在 pydantic 模型中简单地创建一个 Alchemy 会话,这就很容易处理了。
class ForgotPasswordRequestSchema(BaseModel):
email: EmailStr
_session = get_db() # this will simply return the session of database.
_user = None
@validator("email")
def validate_email(cls, v):
# Here I want to query on Users's model to see if the email exist in the
# database. If the email does. not exist then I would like to raise a custom
# python exception as shown below.
user = cls._session.get(Users, email=v) # Here I can use session as I have
# already initialised it as a class variable.
if not user:
cls.session.close()
raise ValueError("Email does not exist in the database.")
cls._user = user # this is because we want to use user object in the request
# function.
cls.session.close()
return v
但这不是一种正确的方法,因为在整个请求过程中只应使用一个会话。正如您在上面的示例中看到的那样,我们正在关闭会话,因此我们将无法在请求函数中将用户对象用作user = payload._user。这意味着我们将不得不再次查询请求函数中的同一行。如果我们不关闭会话,那么我们会看到像这样的炼金术异常 - sqlalchemy.exc.PendingRollbackError。
现在,最好的方法是能够在请求开始时创建并在请求结束时关闭的 Pydantic 模型中使用相同的会话。
所以,我基本上是在寻找一种方法将该会话作为上下文传递给 Pydantic。我的请求函数的会话作为依赖项提供。
【问题讨论】:
-
通常你会使用 FastAPI 中的依赖项来获取任何用户,而不是在 pydantic 验证器中这样做;一般来说,Pydantic 验证器不应该有业务逻辑(在我看来);属于您的应用程序的服务或其他部分。这意味着您将拥有
@app.get, async def reset_password_from_email(user: User = Depends(get_valid_user_from_email):-get_valid_user_from_email之类的东西然后将具有签名并负责从当前数据库中获取任何内容(通过服务)并在必要时生成正确的错误代码。 -
这样服务只关心获取和处理用户,而应用程序依赖关系获取参数、获取用户和生成任何错误,而你的控制器关心“这个端点实际做什么”。
标签: python sqlalchemy fastapi pydantic