Python FastAPI:通过Depends()具有依赖关系和模式引用同一根级别的键,而不会以多个主体参数结束

Python FastAPI:通过Depends()具有依赖关系和模式引用同一根级别的键,而不会以多个主体参数结束,python,fastapi,pydantic,Python,Fastapi,Pydantic,在这种情况下,我希望根据FastAPI路由中的一个值(Organization)授权活动用户。提交特定类型的对象时,其中一个键(organization\u id)应存在,并且应验证用户是否有权访问该组织 我已将其作为API签名中的依赖项解决,以避免在需要访问此属性的所有路由中复制此代码: def get_organization_from_body(organization_id: str = Body(None), user: U

在这种情况下,我希望根据FastAPI路由中的一个值(
Organization
)授权活动用户。提交特定类型的对象时,其中一个键(
organization\u id
)应存在,并且应验证用户是否有权访问该组织

我已将其作为API签名中的依赖项解决,以避免在需要访问此属性的所有路由中复制此代码:

def get_organization_from_body(organization_id: str = Body(None),
                               user: User = Depends(get_authenticated_user),
                               organization_service: OrganizationService = Depends(get_organization_service),
                               ) -> Organization:
    if not organization_id:
        raise HTTPException(status_code=400, detail='Missing organization_id.')

    organization = organization_service.get_organization_for_user(organization_id, user)

    if not organization:
        raise HTTPException(status_code=403, detail='Organization authorization failed.')

    return organization
这可以正常工作,如果API端点希望通过请求中的
organization\u id
键找到一个组织,我可以通过在我的路由中引入
get\u organization\u from\u body
作为依赖项来直接填充该组织:

@router.post('', response_model=Bundle)
async def create_bundle([...]
                        organization: Organization = Depends(get_organization_from_body),
                        ) -> Model:
    
。。如果用户没有访问组织的权限,则会引发403异常

但是,我还希望通过模式模型在根级别包含我的实际对象。因此,我的第一次尝试是发出JSON请求,如下所示:

{
  'name': generated_name,
  'organization_id': created_organization['id_key']
}
然后添加传入的Pydantic模型:

@router.post('', response_model=Bundle)
async def create_bundle(bundle: BundleCreate,
                        organization: Organization = Depends(get_organization_from_body),
                        [...]
                        ) -> BundleModel:
    [...]
    return bundle
无论pydantic模型/架构是否包含
organization\u id
作为字段,结果都是相同的:

class BundleBase(BaseModel):
    name: str

    class Config:
        orm_mode = True


class BundleCreate(BundleBase):
    organization_id: str
    client_id: Optional[str]
。。但是当我介绍我的
get\u organization\u from\u body
依赖项时,FastAPI发现我有另一个引用body字段的依赖项,并且
bundle
对象的描述必须移动到
bundle
键中(因此不是“验证”)在
organization\u id
字段中,JSON布局需要更改-因为我觉得
organization\u id
捆绑包的一部分
描述,它应该在那里。如果可能的话)

错误消息告诉我,
bundle
应作为一个单独的字段:

{'detail': [{'loc': ['body', 'bundle'], 'msg': 'field required', 'type': 'value_error.missing'}]}
正确地说,当我将
name
移动到
bundle
键中时:

{
    'bundle': {
        'name': generated_name,
    },
    'organization_id': created_organization['id_key']
}
。。我的测试通过,请求成功


这可能有点像自行车脱落,但如果有一个快速修复方法可以解决这一限制,我希望找到一种方法来实现验证(通过
dependens()
或以某种替代方式,而不在每个需要该功能的API路由函数中显式执行)与我的输出格式更接近的平面JSON布局。

在FastAPI
0.53.2之前,正文的依赖项已按您尝试的方式解决。此类代码:

class Item(BaseModel):
    val_1: int
    val_2: int


def get_val_1(val_1: int = Body(..., embed=True)):
     return val_1


@app.post("/item", response_model=Item)
def handler(full_body: Item, val_1: int = Depends(get_val_1)):
    return full_body
预期该机构:

{
    "val_1": 0,
    "val_2": 0
}
但是版本
0.53.2
,会自动嵌入不同的主体依赖项(
embed=True
),上面的代码需要以下主体:

{
  "full_body": {
    "val_1": 0,
    "val_2": 0
  },
  "val_1": 0
}
现在,为了能够访问整个实体的模型,并且能够作为单独的依赖项访问其元素,您需要在任何地方对实体模型使用相同的依赖项:

def get_val_1(full_body: Item):
    return full_body.val_1


@app.post("/item", response_model=Item)
def handler(full_body: Item, val_1: int = Depends(get_val_1)):
    return full_body
共享依赖项的更新 您可以为多个模型共享一个主体依赖项,但在这种情况下,必须满足两个条件:依赖项的名称必须相同,并且它们的类型必须兼容(通过继承或不继承)。示例如下:

class Base(BaseModel):
    val_1: int


class NotBase(BaseModel):
    val_1: int


class Item1(Base):
    val_2: int


class Item2(Base):
    val_3: int


def get_val1_base(full_body: Base):
     return full_body.val_1


def get_val1_not_base(full_body: NotBase):
    return full_body.val_1


@app.post("/item1", response_model=Item1)
def handler(full_body: Item1, val_1: int = Depends(get_val1_base)):
    return full_body


@app.post("/item2", response_model=Item2)
def handler(full_body: Item2, val_1: int = Depends(get_val1_not_base)):
    return full_body

谢谢你的解释。我的希望是重新使用依赖关系来解析和验证多个不同模式的依赖关系(例如这里的
Item
),其中使用相同的字段名来表示依赖对象(因此
DifferentItem
也包含
val_1
属性)。我还没有尝试过,但是如果
get\u val_u 1
依赖于Mixin类而不是实际的Item类,那么混入到
Item
DifferentItem
类会起作用吗?(即FASTAPI考虑继承层次结构吗?)是的,您可以为不同的模型共享一个依赖项。我已经更新了答案。但我不能完全肯定这种方法在未来的所有版本中都能起作用。这似乎是一个可行的解决方案。我将对它进行研究,看看是否可以在没有两个不同基类的情况下通过mixin使它工作,但我已经接受了答案。一旦赏金可用,我会立即奖励它。这里只有一个基类,第二个不是基类,是为了完整性而提供的。