diff --git a/docs/en/docs/img/tutorial/request-form-models/image01.png b/docs/en/docs/img/tutorial/request-form-models/image01.png
deleted file mode 100644
index 3fe32c03d..000000000
Binary files a/docs/en/docs/img/tutorial/request-form-models/image01.png and /dev/null differ
diff --git a/docs/en/docs/tutorial/request-form-models.md b/docs/en/docs/tutorial/request-form-models.md
deleted file mode 100644
index 8bb1ffb1f..000000000
--- a/docs/en/docs/tutorial/request-form-models.md
+++ /dev/null
@@ -1,65 +0,0 @@
-# Form Models
-
-You can use Pydantic models to declare form fields in FastAPI.
-
-/// info
-
-To use forms, first install `python-multipart`.
-
-Make sure you create a [virtual environment](../virtual-environments.md){.internal-link target=_blank}, activate it, and then install it, for example:
-
-```console
-$ pip install python-multipart
-```
-
-///
-
-/// note
-
-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`:
-
-//// tab | Python 3.9+
-
-```Python hl_lines="9-11 15"
-{!> ../../../docs_src/request_form_models/tutorial001_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="8-10 14"
-{!> ../../../docs_src/request_form_models/tutorial001_an.py!}
-```
-
-////
-
-//// tab | Python 3.8+ non-Annotated
-
-/// tip
-
-Prefer to use the `Annotated` version if possible.
-
-///
-
-```Python hl_lines="7-9 13"
-{!> ../../../docs_src/request_form_models/tutorial001.py!}
-```
-
-////
-
-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
-
-You can verify it in the docs UI at `/docs`:
-
-
-

-
diff --git a/docs/en/mkdocs.yml b/docs/en/mkdocs.yml
index 7c810c2d7..528c80b8e 100644
--- a/docs/en/mkdocs.yml
+++ b/docs/en/mkdocs.yml
@@ -129,7 +129,6 @@ nav:
- tutorial/extra-models.md
- tutorial/response-status-code.md
- tutorial/request-forms.md
- - tutorial/request-form-models.md
- tutorial/request-files.md
- tutorial/request-forms-and-files.md
- tutorial/handling-errors.md
diff --git a/docs_src/request_form_models/tutorial001.py b/docs_src/request_form_models/tutorial001.py
deleted file mode 100644
index 98feff0b9..000000000
--- a/docs_src/request_form_models/tutorial001.py
+++ /dev/null
@@ -1,14 +0,0 @@
-from fastapi import FastAPI, Form
-from pydantic import BaseModel
-
-app = FastAPI()
-
-
-class FormData(BaseModel):
- username: str
- password: str
-
-
-@app.post("/login/")
-async def login(data: FormData = Form()):
- return data
diff --git a/docs_src/request_form_models/tutorial001_an.py b/docs_src/request_form_models/tutorial001_an.py
deleted file mode 100644
index 30483d445..000000000
--- a/docs_src/request_form_models/tutorial001_an.py
+++ /dev/null
@@ -1,15 +0,0 @@
-from fastapi import FastAPI, Form
-from pydantic import BaseModel
-from typing_extensions import Annotated
-
-app = FastAPI()
-
-
-class FormData(BaseModel):
- username: str
- password: str
-
-
-@app.post("/login/")
-async def login(data: Annotated[FormData, Form()]):
- return data
diff --git a/docs_src/request_form_models/tutorial001_an_py39.py b/docs_src/request_form_models/tutorial001_an_py39.py
deleted file mode 100644
index 7cc81aae9..000000000
--- a/docs_src/request_form_models/tutorial001_an_py39.py
+++ /dev/null
@@ -1,16 +0,0 @@
-from typing import Annotated
-
-from fastapi import FastAPI, Form
-from pydantic import BaseModel
-
-app = FastAPI()
-
-
-class FormData(BaseModel):
- username: str
- password: str
-
-
-@app.post("/login/")
-async def login(data: Annotated[FormData, Form()]):
- return data
diff --git a/fastapi/dependencies/utils.py b/fastapi/dependencies/utils.py
index 98ce17b55..7ac18d941 100644
--- a/fastapi/dependencies/utils.py
+++ b/fastapi/dependencies/utils.py
@@ -33,7 +33,6 @@ from fastapi._compat import (
field_annotation_is_scalar,
get_annotation_from_field_info,
get_missing_field_error,
- get_model_fields,
is_bytes_field,
is_bytes_sequence_field,
is_scalar_field,
@@ -57,7 +56,6 @@ from fastapi.security.base import SecurityBase
from fastapi.security.oauth2 import OAuth2, SecurityScopes
from fastapi.security.open_id_connect_url import OpenIdConnect
from fastapi.utils import create_model_field, get_path_param_names
-from pydantic import BaseModel
from pydantic.fields import FieldInfo
from starlette.background import BackgroundTasks as StarletteBackgroundTasks
from starlette.concurrency import run_in_threadpool
@@ -745,9 +743,7 @@ def _should_embed_body_fields(fields: List[ModelField]) -> bool:
return True
# If it's a Form (or File) field, it has to be a BaseModel to be top level
# otherwise it has to be embedded, so that the key value pair can be extracted
- if isinstance(first_field.field_info, params.Form) and not lenient_issubclass(
- first_field.type_, BaseModel
- ):
+ if isinstance(first_field.field_info, params.Form):
return True
return False
@@ -787,8 +783,7 @@ async def _extract_form_body(
for sub_value in value:
tg.start_soon(process_fn, sub_value.read)
value = serialize_sequence_value(field=field, value=results)
- if value is not None:
- values[field.name] = value
+ values[field.name] = value
return values
@@ -803,14 +798,8 @@ async def request_body_to_args(
single_not_embedded_field = len(body_fields) == 1 and not embed_body_fields
first_field = body_fields[0]
body_to_process = received_body
-
- fields_to_extract: List[ModelField] = body_fields
-
- if single_not_embedded_field and lenient_issubclass(first_field.type_, BaseModel):
- fields_to_extract = get_model_fields(first_field.type_)
-
if isinstance(received_body, FormData):
- body_to_process = await _extract_form_body(fields_to_extract, received_body)
+ body_to_process = await _extract_form_body(body_fields, received_body)
if single_not_embedded_field:
loc: Tuple[str, ...] = ("body",)
diff --git a/scripts/playwright/request_form_models/image01.py b/scripts/playwright/request_form_models/image01.py
deleted file mode 100644
index 15bd3858c..000000000
--- a/scripts/playwright/request_form_models/image01.py
+++ /dev/null
@@ -1,36 +0,0 @@
-import subprocess
-import time
-
-import httpx
-from playwright.sync_api import Playwright, sync_playwright
-
-
-# Run playwright codegen to generate the code below, copy paste the sections in run()
-def run(playwright: Playwright) -> None:
- browser = playwright.chromium.launch(headless=False)
- context = browser.new_context()
- page = context.new_page()
- page.goto("http://localhost:8000/docs")
- page.get_by_role("button", name="POST /login/ Login").click()
- page.get_by_role("button", name="Try it out").click()
- page.screenshot(path="docs/en/docs/img/tutorial/request-form-models/image01.png")
-
- # ---------------------
- context.close()
- browser.close()
-
-
-process = subprocess.Popen(
- ["fastapi", "run", "docs_src/request_form_models/tutorial001.py"]
-)
-try:
- for _ in range(3):
- try:
- response = httpx.get("http://localhost:8000/docs")
- except httpx.ConnectError:
- time.sleep(1)
- break
- with sync_playwright() as playwright:
- run(playwright)
-finally:
- process.terminate()
diff --git a/tests/test_forms_single_model.py b/tests/test_forms_single_model.py
deleted file mode 100644
index 7ed3ba3a2..000000000
--- a/tests/test_forms_single_model.py
+++ /dev/null
@@ -1,129 +0,0 @@
-from typing import List, Optional
-
-from dirty_equals import IsDict
-from fastapi import FastAPI, Form
-from fastapi.testclient import TestClient
-from pydantic import BaseModel
-from typing_extensions import Annotated
-
-app = FastAPI()
-
-
-class FormModel(BaseModel):
- username: str
- lastname: str
- age: Optional[int] = None
- tags: List[str] = ["foo", "bar"]
-
-
-@app.post("/form/")
-def post_form(user: Annotated[FormModel, Form()]):
- return user
-
-
-client = TestClient(app)
-
-
-def test_send_all_data():
- response = client.post(
- "/form/",
- data={
- "username": "Rick",
- "lastname": "Sanchez",
- "age": "70",
- "tags": ["plumbus", "citadel"],
- },
- )
- assert response.status_code == 200, response.text
- assert response.json() == {
- "username": "Rick",
- "lastname": "Sanchez",
- "age": 70,
- "tags": ["plumbus", "citadel"],
- }
-
-
-def test_defaults():
- response = client.post("/form/", data={"username": "Rick", "lastname": "Sanchez"})
- assert response.status_code == 200, response.text
- assert response.json() == {
- "username": "Rick",
- "lastname": "Sanchez",
- "age": None,
- "tags": ["foo", "bar"],
- }
-
-
-def test_invalid_data():
- response = client.post(
- "/form/",
- data={
- "username": "Rick",
- "lastname": "Sanchez",
- "age": "seventy",
- "tags": ["plumbus", "citadel"],
- },
- )
- assert response.status_code == 422, response.text
- assert response.json() == IsDict(
- {
- "detail": [
- {
- "type": "int_parsing",
- "loc": ["body", "age"],
- "msg": "Input should be a valid integer, unable to parse string as an integer",
- "input": "seventy",
- }
- ]
- }
- ) | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "detail": [
- {
- "loc": ["body", "age"],
- "msg": "value is not a valid integer",
- "type": "type_error.integer",
- }
- ]
- }
- )
-
-
-def test_no_data():
- response = client.post("/form/")
- assert response.status_code == 422, response.text
- assert response.json() == IsDict(
- {
- "detail": [
- {
- "type": "missing",
- "loc": ["body", "username"],
- "msg": "Field required",
- "input": {"tags": ["foo", "bar"]},
- },
- {
- "type": "missing",
- "loc": ["body", "lastname"],
- "msg": "Field required",
- "input": {"tags": ["foo", "bar"]},
- },
- ]
- }
- ) | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "detail": [
- {
- "loc": ["body", "username"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- {
- "loc": ["body", "lastname"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- ]
- }
- )
diff --git a/tests/test_tutorial/test_request_form_models/__init__.py b/tests/test_tutorial/test_request_form_models/__init__.py
deleted file mode 100644
index e69de29bb..000000000
diff --git a/tests/test_tutorial/test_request_form_models/test_tutorial001.py b/tests/test_tutorial/test_request_form_models/test_tutorial001.py
deleted file mode 100644
index 46c130ee8..000000000
--- a/tests/test_tutorial/test_request_form_models/test_tutorial001.py
+++ /dev/null
@@ -1,232 +0,0 @@
-import pytest
-from dirty_equals import IsDict
-from fastapi.testclient import TestClient
-
-
-@pytest.fixture(name="client")
-def get_client():
- from docs_src.request_form_models.tutorial001 import app
-
- client = TestClient(app)
- return client
-
-
-def test_post_body_form(client: TestClient):
- response = client.post("/login/", data={"username": "Foo", "password": "secret"})
- assert response.status_code == 200
- assert response.json() == {"username": "Foo", "password": "secret"}
-
-
-def test_post_body_form_no_password(client: TestClient):
- response = client.post("/login/", data={"username": "Foo"})
- assert response.status_code == 422
- assert response.json() == IsDict(
- {
- "detail": [
- {
- "type": "missing",
- "loc": ["body", "password"],
- "msg": "Field required",
- "input": {"username": "Foo"},
- }
- ]
- }
- ) | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "detail": [
- {
- "loc": ["body", "password"],
- "msg": "field required",
- "type": "value_error.missing",
- }
- ]
- }
- )
-
-
-def test_post_body_form_no_username(client: TestClient):
- response = client.post("/login/", data={"password": "secret"})
- assert response.status_code == 422
- assert response.json() == IsDict(
- {
- "detail": [
- {
- "type": "missing",
- "loc": ["body", "username"],
- "msg": "Field required",
- "input": {"password": "secret"},
- }
- ]
- }
- ) | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "detail": [
- {
- "loc": ["body", "username"],
- "msg": "field required",
- "type": "value_error.missing",
- }
- ]
- }
- )
-
-
-def test_post_body_form_no_data(client: TestClient):
- response = client.post("/login/")
- assert response.status_code == 422
- assert response.json() == IsDict(
- {
- "detail": [
- {
- "type": "missing",
- "loc": ["body", "username"],
- "msg": "Field required",
- "input": {},
- },
- {
- "type": "missing",
- "loc": ["body", "password"],
- "msg": "Field required",
- "input": {},
- },
- ]
- }
- ) | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "detail": [
- {
- "loc": ["body", "username"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- {
- "loc": ["body", "password"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- ]
- }
- )
-
-
-def test_post_body_json(client: TestClient):
- response = client.post("/login/", json={"username": "Foo", "password": "secret"})
- assert response.status_code == 422, response.text
- assert response.json() == IsDict(
- {
- "detail": [
- {
- "type": "missing",
- "loc": ["body", "username"],
- "msg": "Field required",
- "input": {},
- },
- {
- "type": "missing",
- "loc": ["body", "password"],
- "msg": "Field required",
- "input": {},
- },
- ]
- }
- ) | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "detail": [
- {
- "loc": ["body", "username"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- {
- "loc": ["body", "password"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- ]
- }
- )
-
-
-def test_openapi_schema(client: TestClient):
- response = client.get("/openapi.json")
- assert response.status_code == 200, response.text
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/login/": {
- "post": {
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- "summary": "Login",
- "operationId": "login_login__post",
- "requestBody": {
- "content": {
- "application/x-www-form-urlencoded": {
- "schema": {"$ref": "#/components/schemas/FormData"}
- }
- },
- "required": True,
- },
- }
- }
- },
- "components": {
- "schemas": {
- "FormData": {
- "properties": {
- "username": {"type": "string", "title": "Username"},
- "password": {"type": "string", "title": "Password"},
- },
- "type": "object",
- "required": ["username", "password"],
- "title": "FormData",
- },
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
- },
- "HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
- "properties": {
- "detail": {
- "title": "Detail",
- "type": "array",
- "items": {"$ref": "#/components/schemas/ValidationError"},
- }
- },
- },
- }
- },
- }
diff --git a/tests/test_tutorial/test_request_form_models/test_tutorial001_an.py b/tests/test_tutorial/test_request_form_models/test_tutorial001_an.py
deleted file mode 100644
index 4e14d89c8..000000000
--- a/tests/test_tutorial/test_request_form_models/test_tutorial001_an.py
+++ /dev/null
@@ -1,232 +0,0 @@
-import pytest
-from dirty_equals import IsDict
-from fastapi.testclient import TestClient
-
-
-@pytest.fixture(name="client")
-def get_client():
- from docs_src.request_form_models.tutorial001_an import app
-
- client = TestClient(app)
- return client
-
-
-def test_post_body_form(client: TestClient):
- response = client.post("/login/", data={"username": "Foo", "password": "secret"})
- assert response.status_code == 200
- assert response.json() == {"username": "Foo", "password": "secret"}
-
-
-def test_post_body_form_no_password(client: TestClient):
- response = client.post("/login/", data={"username": "Foo"})
- assert response.status_code == 422
- assert response.json() == IsDict(
- {
- "detail": [
- {
- "type": "missing",
- "loc": ["body", "password"],
- "msg": "Field required",
- "input": {"username": "Foo"},
- }
- ]
- }
- ) | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "detail": [
- {
- "loc": ["body", "password"],
- "msg": "field required",
- "type": "value_error.missing",
- }
- ]
- }
- )
-
-
-def test_post_body_form_no_username(client: TestClient):
- response = client.post("/login/", data={"password": "secret"})
- assert response.status_code == 422
- assert response.json() == IsDict(
- {
- "detail": [
- {
- "type": "missing",
- "loc": ["body", "username"],
- "msg": "Field required",
- "input": {"password": "secret"},
- }
- ]
- }
- ) | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "detail": [
- {
- "loc": ["body", "username"],
- "msg": "field required",
- "type": "value_error.missing",
- }
- ]
- }
- )
-
-
-def test_post_body_form_no_data(client: TestClient):
- response = client.post("/login/")
- assert response.status_code == 422
- assert response.json() == IsDict(
- {
- "detail": [
- {
- "type": "missing",
- "loc": ["body", "username"],
- "msg": "Field required",
- "input": {},
- },
- {
- "type": "missing",
- "loc": ["body", "password"],
- "msg": "Field required",
- "input": {},
- },
- ]
- }
- ) | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "detail": [
- {
- "loc": ["body", "username"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- {
- "loc": ["body", "password"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- ]
- }
- )
-
-
-def test_post_body_json(client: TestClient):
- response = client.post("/login/", json={"username": "Foo", "password": "secret"})
- assert response.status_code == 422, response.text
- assert response.json() == IsDict(
- {
- "detail": [
- {
- "type": "missing",
- "loc": ["body", "username"],
- "msg": "Field required",
- "input": {},
- },
- {
- "type": "missing",
- "loc": ["body", "password"],
- "msg": "Field required",
- "input": {},
- },
- ]
- }
- ) | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "detail": [
- {
- "loc": ["body", "username"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- {
- "loc": ["body", "password"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- ]
- }
- )
-
-
-def test_openapi_schema(client: TestClient):
- response = client.get("/openapi.json")
- assert response.status_code == 200, response.text
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/login/": {
- "post": {
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- "summary": "Login",
- "operationId": "login_login__post",
- "requestBody": {
- "content": {
- "application/x-www-form-urlencoded": {
- "schema": {"$ref": "#/components/schemas/FormData"}
- }
- },
- "required": True,
- },
- }
- }
- },
- "components": {
- "schemas": {
- "FormData": {
- "properties": {
- "username": {"type": "string", "title": "Username"},
- "password": {"type": "string", "title": "Password"},
- },
- "type": "object",
- "required": ["username", "password"],
- "title": "FormData",
- },
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
- },
- "HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
- "properties": {
- "detail": {
- "title": "Detail",
- "type": "array",
- "items": {"$ref": "#/components/schemas/ValidationError"},
- }
- },
- },
- }
- },
- }
diff --git a/tests/test_tutorial/test_request_form_models/test_tutorial001_an_py39.py b/tests/test_tutorial/test_request_form_models/test_tutorial001_an_py39.py
deleted file mode 100644
index 2e6426aa7..000000000
--- a/tests/test_tutorial/test_request_form_models/test_tutorial001_an_py39.py
+++ /dev/null
@@ -1,240 +0,0 @@
-import pytest
-from dirty_equals import IsDict
-from fastapi.testclient import TestClient
-
-from tests.utils import needs_py39
-
-
-@pytest.fixture(name="client")
-def get_client():
- from docs_src.request_form_models.tutorial001_an_py39 import app
-
- client = TestClient(app)
- return client
-
-
-@needs_py39
-def test_post_body_form(client: TestClient):
- response = client.post("/login/", data={"username": "Foo", "password": "secret"})
- assert response.status_code == 200
- assert response.json() == {"username": "Foo", "password": "secret"}
-
-
-@needs_py39
-def test_post_body_form_no_password(client: TestClient):
- response = client.post("/login/", data={"username": "Foo"})
- assert response.status_code == 422
- assert response.json() == IsDict(
- {
- "detail": [
- {
- "type": "missing",
- "loc": ["body", "password"],
- "msg": "Field required",
- "input": {"username": "Foo"},
- }
- ]
- }
- ) | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "detail": [
- {
- "loc": ["body", "password"],
- "msg": "field required",
- "type": "value_error.missing",
- }
- ]
- }
- )
-
-
-@needs_py39
-def test_post_body_form_no_username(client: TestClient):
- response = client.post("/login/", data={"password": "secret"})
- assert response.status_code == 422
- assert response.json() == IsDict(
- {
- "detail": [
- {
- "type": "missing",
- "loc": ["body", "username"],
- "msg": "Field required",
- "input": {"password": "secret"},
- }
- ]
- }
- ) | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "detail": [
- {
- "loc": ["body", "username"],
- "msg": "field required",
- "type": "value_error.missing",
- }
- ]
- }
- )
-
-
-@needs_py39
-def test_post_body_form_no_data(client: TestClient):
- response = client.post("/login/")
- assert response.status_code == 422
- assert response.json() == IsDict(
- {
- "detail": [
- {
- "type": "missing",
- "loc": ["body", "username"],
- "msg": "Field required",
- "input": {},
- },
- {
- "type": "missing",
- "loc": ["body", "password"],
- "msg": "Field required",
- "input": {},
- },
- ]
- }
- ) | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "detail": [
- {
- "loc": ["body", "username"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- {
- "loc": ["body", "password"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- ]
- }
- )
-
-
-@needs_py39
-def test_post_body_json(client: TestClient):
- response = client.post("/login/", json={"username": "Foo", "password": "secret"})
- assert response.status_code == 422, response.text
- assert response.json() == IsDict(
- {
- "detail": [
- {
- "type": "missing",
- "loc": ["body", "username"],
- "msg": "Field required",
- "input": {},
- },
- {
- "type": "missing",
- "loc": ["body", "password"],
- "msg": "Field required",
- "input": {},
- },
- ]
- }
- ) | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "detail": [
- {
- "loc": ["body", "username"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- {
- "loc": ["body", "password"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- ]
- }
- )
-
-
-@needs_py39
-def test_openapi_schema(client: TestClient):
- response = client.get("/openapi.json")
- assert response.status_code == 200, response.text
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/login/": {
- "post": {
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- "summary": "Login",
- "operationId": "login_login__post",
- "requestBody": {
- "content": {
- "application/x-www-form-urlencoded": {
- "schema": {"$ref": "#/components/schemas/FormData"}
- }
- },
- "required": True,
- },
- }
- }
- },
- "components": {
- "schemas": {
- "FormData": {
- "properties": {
- "username": {"type": "string", "title": "Username"},
- "password": {"type": "string", "title": "Password"},
- },
- "type": "object",
- "required": ["username", "password"],
- "title": "FormData",
- },
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
- },
- "HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
- "properties": {
- "detail": {
- "title": "Detail",
- "type": "array",
- "items": {"$ref": "#/components/schemas/ValidationError"},
- }
- },
- },
- }
- },
- }