Skip to content

Commit

Permalink
✨ Add support for forbidding extra form fields with Pydantic models (f…
Browse files Browse the repository at this point in the history
…astapi#12134)

Co-authored-by: Sofie Van Landeghem <svlandeg@users.noreply.github.com>
  • Loading branch information
tiangolo and svlandeg authored Sep 6, 2024
1 parent 1b06b53 commit 4633b1b
Show file tree
Hide file tree
Showing 14 changed files with 1,360 additions and 3 deletions.
75 changes: 72 additions & 3 deletions docs/en/docs/tutorial/request-form-models.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Form Models

You can use Pydantic models to declare form fields in FastAPI.
You can use **Pydantic models** to declare **form fields** in FastAPI.

/// info

Expand All @@ -22,7 +22,7 @@ This is supported since FastAPI version `0.113.0`. 🤓

## Pydantic Models for Forms

You just need to declare a Pydantic model with the fields you want to receive as form fields, and then declare the parameter as `Form`:
You just need to declare a **Pydantic model** with the fields you want to receive as **form fields**, and then declare the parameter as `Form`:

//// tab | Python 3.9+

Expand Down Expand Up @@ -54,7 +54,7 @@ Prefer to use the `Annotated` version if possible.

////

FastAPI will extract the data for each field from the form data in the request and give you the Pydantic model you defined.
**FastAPI** will **extract** the data for **each field** from the **form data** in the request and give you the Pydantic model you defined.

## Check the Docs

Expand All @@ -63,3 +63,72 @@ You can verify it in the docs UI at `/docs`:
<div class="screenshot">
<img src="/img/tutorial/request-form-models/image01.png">
</div>

## Restrict Extra Form Fields

In some special use cases (probably not very common), you might want to **restrict** the form fields to only those declared in the Pydantic model. And **forbid** any **extra** fields.

/// note

This is supported since FastAPI version `0.114.0`. 🤓

///

You can use Pydantic's model configuration to `forbid` any `extra` fields:

//// tab | Python 3.9+

```Python hl_lines="12"
{!> ../../../docs_src/request_form_models/tutorial002_an_py39.py!}
```

////

//// tab | Python 3.8+

```Python hl_lines="11"
{!> ../../../docs_src/request_form_models/tutorial002_an.py!}
```

////

//// tab | Python 3.8+ non-Annotated

/// tip

Prefer to use the `Annotated` version if possible.

///

```Python hl_lines="10"
{!> ../../../docs_src/request_form_models/tutorial002.py!}
```

////

If a client tries to send some extra data, they will receive an **error** response.

For example, if the client tries to send the form fields:

* `username`: `Rick`
* `password`: `Portal Gun`
* `extra`: `Mr. Poopybutthole`

They will receive an error response telling them that the field `extra` is not allowed:

```json
{
"detail": [
{
"type": "extra_forbidden",
"loc": ["body", "extra"],
"msg": "Extra inputs are not permitted",
"input": "Mr. Poopybutthole"
}
]
}
```

## Summary

You can use Pydantic models to declare form fields in FastAPI. 😎
15 changes: 15 additions & 0 deletions docs_src/request_form_models/tutorial002.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from fastapi import FastAPI, Form
from pydantic import BaseModel

app = FastAPI()


class FormData(BaseModel):
username: str
password: str
model_config = {"extra": "forbid"}


@app.post("/login/")
async def login(data: FormData = Form()):
return data
16 changes: 16 additions & 0 deletions docs_src/request_form_models/tutorial002_an.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from fastapi import FastAPI, Form
from pydantic import BaseModel
from typing_extensions import Annotated

app = FastAPI()


class FormData(BaseModel):
username: str
password: str
model_config = {"extra": "forbid"}


@app.post("/login/")
async def login(data: Annotated[FormData, Form()]):
return data
17 changes: 17 additions & 0 deletions docs_src/request_form_models/tutorial002_an_py39.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from typing import Annotated

from fastapi import FastAPI, Form
from pydantic import BaseModel

app = FastAPI()


class FormData(BaseModel):
username: str
password: str
model_config = {"extra": "forbid"}


@app.post("/login/")
async def login(data: Annotated[FormData, Form()]):
return data
17 changes: 17 additions & 0 deletions docs_src/request_form_models/tutorial002_pv1.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from fastapi import FastAPI, Form
from pydantic import BaseModel

app = FastAPI()


class FormData(BaseModel):
username: str
password: str

class Config:
extra = "forbid"


@app.post("/login/")
async def login(data: FormData = Form()):
return data
18 changes: 18 additions & 0 deletions docs_src/request_form_models/tutorial002_pv1_an.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
from fastapi import FastAPI, Form
from pydantic import BaseModel
from typing_extensions import Annotated

app = FastAPI()


class FormData(BaseModel):
username: str
password: str

class Config:
extra = "forbid"


@app.post("/login/")
async def login(data: Annotated[FormData, Form()]):
return data
19 changes: 19 additions & 0 deletions docs_src/request_form_models/tutorial002_pv1_an_py39.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from typing import Annotated

from fastapi import FastAPI, Form
from pydantic import BaseModel

app = FastAPI()


class FormData(BaseModel):
username: str
password: str

class Config:
extra = "forbid"


@app.post("/login/")
async def login(data: Annotated[FormData, Form()]):
return data
3 changes: 3 additions & 0 deletions fastapi/dependencies/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -789,6 +789,9 @@ async def process_fn(
value = serialize_sequence_value(field=field, value=results)
if value is not None:
values[field.name] = value
for key, value in received_body.items():
if key not in values:
values[key] = value
return values


Expand Down
Loading

0 comments on commit 4633b1b

Please sign in to comment.