mirror of https://github.com/tiangolo/fastapi.git
🎨 [pre-commit.ci] Auto format from pre-commit.com hooks
This commit is contained in:
parent
f63e983b60
commit
cad08bbc4d
|
|
@ -97,7 +97,7 @@ and the JSON response is also correct:
|
|||
```
|
||||
|
||||
When the checkbox is *unchecked*, though, something strange happens.
|
||||
The submitted form data is *empty*,
|
||||
The submitted form data is *empty*,
|
||||
and the returned JSON data still shows `checkbox` still being `true`!
|
||||
|
||||
This is because checkboxes in HTML forms don't work exactly like the boolean inputs we expect,
|
||||
|
|
@ -108,7 +108,7 @@ When dealing with form models with defaults,
|
|||
we need to take special care to handle cases where the field being *unset* has a specific meaning.
|
||||
|
||||
In some cases, we can resolve the problem by changing or removing the default,
|
||||
but we don't always have that option -
|
||||
but we don't always have that option -
|
||||
particularly when the model is used in other places than the form
|
||||
(model reuse is one of the benefits of building FastAPI on top of pydantic, after all!).
|
||||
|
||||
|
|
@ -116,8 +116,8 @@ To do this, you can use a [`model_validator`](https://docs.pydantic.dev/latest/c
|
|||
in the `before` mode - before the defaults from the model are applied,
|
||||
to differentiate between an explicit `False` value and an unset value.
|
||||
|
||||
We also don't want to just treat any time the value is unset as ``False`` -
|
||||
that would defeat the purpose of the default!
|
||||
We also don't want to just treat any time the value is unset as ``False`` -
|
||||
that would defeat the purpose of the default!
|
||||
We want to specifically correct the behavior when it is used in the context of a *form.*
|
||||
|
||||
So we can additionally use the `'fastapi_field'` passed to the
|
||||
|
|
|
|||
|
|
@ -1,12 +1,14 @@
|
|||
from pydantic import BaseModel
|
||||
from fastapi import FastAPI, Form, Request
|
||||
from fastapi.responses import HTMLResponse
|
||||
from fastapi.templating import Jinja2Templates
|
||||
from jinja2 import DictLoader, Environment
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class MyModel(BaseModel):
|
||||
checkbox: bool = True
|
||||
|
||||
|
||||
form_template = """
|
||||
<form action="/form" method="POST">
|
||||
{% for field_name, field in model.model_fields.items() %}
|
||||
|
|
@ -31,12 +33,14 @@ templates = Jinja2Templates(env=Environment(loader=loader))
|
|||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/form", response_class=HTMLResponse)
|
||||
async def show_form(request: Request):
|
||||
return templates.TemplateResponse(
|
||||
request=request, name="form.html", context={"model": MyModel}
|
||||
)
|
||||
|
||||
@app.post('/form')
|
||||
|
||||
@app.post("/form")
|
||||
async def submit_form(data: MyModel = Form()) -> MyModel:
|
||||
return data
|
||||
|
|
|
|||
|
|
@ -1,14 +1,16 @@
|
|||
from typing import Annotated
|
||||
|
||||
from pydantic import BaseModel
|
||||
from fastapi import FastAPI, Form, Request
|
||||
from fastapi.responses import HTMLResponse
|
||||
from fastapi.templating import Jinja2Templates
|
||||
from jinja2 import DictLoader, Environment
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class MyModel(BaseModel):
|
||||
checkbox: bool = True
|
||||
|
||||
|
||||
form_template = """
|
||||
<form action="/form" method="POST">
|
||||
{% for field_name, field in model.model_fields.items() %}
|
||||
|
|
@ -16,7 +18,7 @@ form_template = """
|
|||
<label for="{{ field_name }}">{{ field_name }}</label>
|
||||
{% if field.annotation.__name__ == "bool" %}
|
||||
<input type="checkbox" name="{{field_name}}"
|
||||
{% if field.default %}
|
||||
{% if field.default %}
|
||||
checked="checked"
|
||||
{% endif %}
|
||||
>
|
||||
|
|
@ -33,12 +35,14 @@ templates = Jinja2Templates(env=Environment(loader=loader))
|
|||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/form", response_class=HTMLResponse)
|
||||
async def show_form(request: Request):
|
||||
return templates.TemplateResponse(
|
||||
request=request, name="form.html", context={"model": MyModel}
|
||||
)
|
||||
|
||||
@app.post('/form')
|
||||
|
||||
@app.post("/form")
|
||||
async def submit_form(data: Annotated[MyModel, Form()]) -> MyModel:
|
||||
return data
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
from pydantic import BaseModel, ValidationInfo, model_validator
|
||||
from fastapi import FastAPI, Form, Request
|
||||
from fastapi.responses import HTMLResponse
|
||||
from fastapi.templating import Jinja2Templates
|
||||
from jinja2 import DictLoader, Environment
|
||||
from pydantic import BaseModel, ValidationInfo, model_validator
|
||||
|
||||
|
||||
class MyModel(BaseModel):
|
||||
checkbox: bool = True
|
||||
|
|
@ -10,15 +11,15 @@ class MyModel(BaseModel):
|
|||
@model_validator(mode="before")
|
||||
def handle_defaults(cls, value: dict, info: ValidationInfo) -> dict:
|
||||
# if this model is being used outside of fastapi, return normally
|
||||
if info.context is None or 'fastapi_field' not in info.context:
|
||||
if info.context is None or "fastapi_field" not in info.context:
|
||||
return value
|
||||
|
||||
# check if we are being validated from form input,
|
||||
# and if so, treat the unset checkbox as False
|
||||
field_info = info.context['fastapi_field'].field_info
|
||||
field_info = info.context["fastapi_field"].field_info
|
||||
is_form = type(field_info).__name__ == "Form"
|
||||
if is_form and 'checkbox' not in value:
|
||||
value['checkbox'] = False
|
||||
if is_form and "checkbox" not in value:
|
||||
value["checkbox"] = False
|
||||
return value
|
||||
|
||||
|
||||
|
|
@ -29,7 +30,7 @@ form_template = """
|
|||
<label for="{{ field_name }}">{{ field_name }}</label>
|
||||
{% if field.annotation.__name__ == "bool" %}
|
||||
<input type="checkbox" name="{{field_name}}"
|
||||
{% if field.default %}
|
||||
{% if field.default %}
|
||||
checked="checked"
|
||||
{% endif %}
|
||||
>
|
||||
|
|
@ -46,12 +47,14 @@ templates = Jinja2Templates(env=Environment(loader=loader))
|
|||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/form", response_class=HTMLResponse)
|
||||
async def show_form(request: Request):
|
||||
return templates.TemplateResponse(
|
||||
request=request, name="form.html", context={"model": MyModel}
|
||||
)
|
||||
|
||||
@app.post('/form')
|
||||
|
||||
@app.post("/form")
|
||||
async def submit_form(data: MyModel = Form()) -> MyModel:
|
||||
return data
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
from typing import Annotated
|
||||
|
||||
from pydantic import BaseModel, ValidationInfo, model_validator
|
||||
from fastapi import FastAPI, Form, Request
|
||||
from fastapi.responses import HTMLResponse
|
||||
from fastapi.templating import Jinja2Templates
|
||||
from jinja2 import DictLoader, Environment
|
||||
from pydantic import BaseModel, ValidationInfo, model_validator
|
||||
|
||||
|
||||
class MyModel(BaseModel):
|
||||
checkbox: bool = True
|
||||
|
|
@ -12,15 +13,15 @@ class MyModel(BaseModel):
|
|||
@model_validator(mode="before")
|
||||
def handle_defaults(cls, value: dict, info: ValidationInfo) -> dict:
|
||||
# if this model is being used outside of fastapi, return normally
|
||||
if info.context is None or 'fastapi_field' not in info.context:
|
||||
if info.context is None or "fastapi_field" not in info.context:
|
||||
return value
|
||||
|
||||
# check if we are being validated from form input,
|
||||
# and if so, treat the unset checkbox as False
|
||||
field_info = info.context['fastapi_field'].field_info
|
||||
field_info = info.context["fastapi_field"].field_info
|
||||
is_form = type(field_info).__name__ == "Form"
|
||||
if is_form and 'checkbox' not in value:
|
||||
value['checkbox'] = False
|
||||
if is_form and "checkbox" not in value:
|
||||
value["checkbox"] = False
|
||||
return value
|
||||
|
||||
|
||||
|
|
@ -31,7 +32,7 @@ form_template = """
|
|||
<label for="{{ field_name }}">{{ field_name }}</label>
|
||||
{% if field.annotation.__name__ == "bool" %}
|
||||
<input type="checkbox" name="{{field_name}}"
|
||||
{% if field.default %}
|
||||
{% if field.default %}
|
||||
checked="checked"
|
||||
{% endif %}
|
||||
>
|
||||
|
|
@ -48,12 +49,14 @@ templates = Jinja2Templates(env=Environment(loader=loader))
|
|||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/form", response_class=HTMLResponse)
|
||||
async def show_form(request: Request):
|
||||
return templates.TemplateResponse(
|
||||
request=request, name="form.html", context={"model": MyModel}
|
||||
)
|
||||
|
||||
@app.post('/form')
|
||||
|
||||
@app.post("/form")
|
||||
async def submit_form(data: Annotated[MyModel, Form()]) -> MyModel:
|
||||
return data
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
from pydantic import BaseModel, model_validator
|
||||
from fastapi import FastAPI, Form, Request
|
||||
from fastapi.responses import HTMLResponse
|
||||
from fastapi.templating import Jinja2Templates
|
||||
from jinja2 import DictLoader, Environment
|
||||
from pydantic import BaseModel, model_validator
|
||||
|
||||
|
||||
class MyModel(BaseModel):
|
||||
checkbox: bool = True
|
||||
|
|
@ -11,8 +12,8 @@ class MyModel(BaseModel):
|
|||
def handle_defaults(cls, value: dict) -> dict:
|
||||
# We can't tell if we're being validated by fastAPI,
|
||||
# so we have to just YOLO this.
|
||||
if 'checkbox' not in value:
|
||||
value['checkbox'] = False
|
||||
if "checkbox" not in value:
|
||||
value["checkbox"] = False
|
||||
return value
|
||||
|
||||
|
||||
|
|
@ -23,7 +24,7 @@ form_template = """
|
|||
<label for="{{ field_name }}">{{ field_name }}</label>
|
||||
{% if field.annotation.__name__ == "bool" %}
|
||||
<input type="checkbox" name="{{field_name}}"
|
||||
{% if field.default %}
|
||||
{% if field.default %}
|
||||
checked="checked"
|
||||
{% endif %}
|
||||
>
|
||||
|
|
@ -40,12 +41,14 @@ templates = Jinja2Templates(env=Environment(loader=loader))
|
|||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/form", response_class=HTMLResponse)
|
||||
async def show_form(request: Request):
|
||||
return templates.TemplateResponse(
|
||||
request=request, name="form.html", context={"model": MyModel}
|
||||
)
|
||||
|
||||
@app.post('/form')
|
||||
|
||||
@app.post("/form")
|
||||
async def submit_form(data: MyModel = Form()) -> MyModel:
|
||||
return data
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
from typing import Annotated
|
||||
|
||||
from pydantic import BaseModel, model_validator
|
||||
from fastapi import FastAPI, Form, Request
|
||||
from fastapi.responses import HTMLResponse
|
||||
from fastapi.templating import Jinja2Templates
|
||||
from jinja2 import DictLoader, Environment
|
||||
from pydantic import BaseModel, model_validator
|
||||
|
||||
|
||||
class MyModel(BaseModel):
|
||||
checkbox: bool = True
|
||||
|
|
@ -13,8 +14,8 @@ class MyModel(BaseModel):
|
|||
def handle_defaults(cls, value: dict) -> dict:
|
||||
# We can't tell if we're being validated by fastAPI,
|
||||
# so we have to just YOLO this.
|
||||
if 'checkbox' not in value:
|
||||
value['checkbox'] = False
|
||||
if "checkbox" not in value:
|
||||
value["checkbox"] = False
|
||||
return value
|
||||
|
||||
|
||||
|
|
@ -25,7 +26,7 @@ form_template = """
|
|||
<label for="{{ field_name }}">{{ field_name }}</label>
|
||||
{% if field.annotation.__name__ == "bool" %}
|
||||
<input type="checkbox" name="{{field_name}}"
|
||||
{% if field.default %}
|
||||
{% if field.default %}
|
||||
checked="checked"
|
||||
{% endif %}
|
||||
>
|
||||
|
|
@ -42,12 +43,14 @@ templates = Jinja2Templates(env=Environment(loader=loader))
|
|||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/form", response_class=HTMLResponse)
|
||||
async def show_form(request: Request):
|
||||
return templates.TemplateResponse(
|
||||
request=request, name="form.html", context={"model": MyModel}
|
||||
)
|
||||
|
||||
@app.post('/form')
|
||||
|
||||
@app.post("/form")
|
||||
async def submit_form(data: Annotated[MyModel, Form()]) -> MyModel:
|
||||
return data
|
||||
|
|
|
|||
|
|
@ -126,7 +126,9 @@ if PYDANTIC_V2:
|
|||
) -> Tuple[Any, Union[List[Dict[str, Any]], None]]:
|
||||
try:
|
||||
return (
|
||||
self._type_adapter.validate_python(value, from_attributes=True, context={"fastapi_field": self}),
|
||||
self._type_adapter.validate_python(
|
||||
value, from_attributes=True, context={"fastapi_field": self}
|
||||
),
|
||||
None,
|
||||
)
|
||||
except ValidationError as exc:
|
||||
|
|
|
|||
Loading…
Reference in New Issue