fastapi
fastapi copied to clipboard
`Body` related parameters don't take `alias` in consideration
Privileged issue
- [X] I'm @tiangolo or he asked me directly to create an issue here.
Issue Content
The Body
, File
, and Form
parameters don't support alias
.
The following snippet demonstrates the issue:
from typing import Annotated
from fastapi import Body, Cookie, FastAPI, Form, UploadFile, File, Query, Header, Path
app = FastAPI()
@app.post("/{path}")
def endpoint(
path: Annotated[int, Path(alias="PathAlias")],
cookie: Annotated[int, Cookie(alias="CookieAlias")],
header: Annotated[int, Header(alias="HeaderAlias")],
query: Annotated[int, Query(alias="QueryAlias")],
body: Annotated[int, Body(alias="BodyAlias")],
form: Annotated[int, Form(alias="FormAlias")],
file: Annotated[UploadFile, File(alias="FileAlias")],
):
...
If you look at the generated Swagger you see that alias
is only being used for params.Param
related fields i.e. Path
, Cookie
, etc.
People can overcome this right now using validation_alias
as follows:
from typing import Annotated
from fastapi import Body, Cookie, FastAPI, Form, UploadFile, File, Query, Header, Path
app = FastAPI()
@app.post("/{path}")
def endpoint(
path: Annotated[int, Path(alias="PathAlias")],
cookie: Annotated[int, Cookie(alias="CookieAlias")],
header: Annotated[int, Header(alias="HeaderAlias")],
query: Annotated[int, Query(alias="QueryAlias")],
body: Annotated[int, Body(validation_alias="BodyAlias")],
form: Annotated[int, Form(validation_alias="FormAlias")],
file: Annotated[UploadFile, File(validation_alias="FileAlias")],
):
...
In any case, this should be fixed in FastAPI.
When I use only validation_alias in Form I can see alias in Swagger, but the validation is not working well. For the moment I'm using both alias and validation_alias, It works.
I think issue is as FastAPI pass validation_alias to FieldInfo of Pydantic v2 in super().init () inside class Body
Here is one way to fix this issue in FastAPI:
- Add support for
alias
inBody
,Form
, andFile
parameters similar to how it is already supported inPath
,Cookie
, etc.
This would involve:
-
Updating the
Body
,Form
, andFile
classes to accept analias
argument. -
Plumbing the
alias
through to OpenAPI/Swagger schema generation. -
Updating the documentation to mention that
alias
is now supported.
- Handle conflicts if
alias
andvalidation_alias
are both provided.
For example, raise an exception if they don't match or take precedence of one over the other.
-
Add tests to validate the new
alias
functionality. -
Submit a PR with the changes to the main FastAPI repo.
This would fix the issue so alias
can be used directly instead of having to use validation_alias
as a workaround. It keeps the API consistent across all the different parameter types.
I request a PR #10319 to fix it. Now I can use alias in Form and File
It looks like this behavior might extend to Param
types during validation. Here's an example that includes Query
, Path
, Header
, and Cookie
, compared to Pydantic's Field
:
from fastapi import Query, Path, Header, Cookie
from pydantic import BaseModel, ConfigDict, Field
class ForbidExtra(BaseModel):
model_config = ConfigDict(extra="forbid")
class FieldModel(ForbidExtra):
type_: str | None = Field(default=None, alias="type_alias")
class QueryModel(ForbidExtra):
type_: str | None = Query(default=None, alias="type_alias")
class CookieModel(ForbidExtra):
type_: str | None = Cookie(default=None, alias="type_alias")
class HeaderModel(ForbidExtra):
type_: str | None = Header(default=None, alias="type_alias")
class PathModel(ForbidExtra):
type_: str | None = Path(alias="type_alias")
field = FieldModel(type_alias="alias") # This behaves as expected.
# But these raise ValidationError:
query = QueryModel(type_alias="alias")
cookie = CookieModel(type_alias="alias")
header = HeaderModel(type_alias="alias")
path = PathModel(type_alias="alias")
@harol97, the workaround didn't work for me, am I doing something wrong?
for route in app.routes:
if isinstance(route, APIRoute):
# TODO: body params aliases won't work in Swagger - https://github.com/tiangolo/fastapi/issues/10286
_transform_snake_case(route.dependant.body_params)
_transform_snake_case(route.dependant.query_params)
_transform_snake_case(route.dependant.header_params)```
def _transform_snake_case(params: list[ModelField]) -> None:
for param in params:
camelized = humps.camelize(param.name)
param.field_info.alias = camelized
param.field_info.validation_alias = camelized
@hruzeda you could first do: camelized = humps.camelize(param.field_info.alias)
Hi, as @Kludex told me to watch this issue I wanted to make sure if I apply the workaround correctly as it is not (maybe no longer, not 100% sure on that) working. The context you can find in this discussion: https://github.com/tiangolo/fastapi/discussions/11004
I tried:
async def upload_docs(
files: list[UploadFile] = File(..., validation_alias="file")
) -> dict:
and this
async def upload_docs(
files: list[UploadFile] = File(..., alias="file", validation_alias="file")
) -> dict:
without success.
I want my multipart form data file upload to allow for either file or files in the post body to be compatible with some legacy systems.