From 7bad7c09757f8a06cf62cc0838082a766065883e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= <tiangolo@gmail.com> Date: Thu, 5 Sep 2024 17:16:50 +0200 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Add=20support=20for=20Pydantic=20mo?= =?UTF-8?q?dels=20in=20`Form`=20parameters=20(#12129)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Revert "⏪️ Temporarily revert "✨ Add support for Pydantic models in `Form` pa…" This reverts commit 8e6cf9ee9c9d87b6b658cc240146121c80f71476. --- .../tutorial/request-form-models/image01.png | Bin 0 -> 44487 bytes docs/en/docs/tutorial/request-form-models.md | 65 +++++ docs/en/mkdocs.yml | 1 + docs_src/request_form_models/tutorial001.py | 14 + .../request_form_models/tutorial001_an.py | 15 ++ .../tutorial001_an_py39.py | 16 ++ fastapi/dependencies/utils.py | 17 +- .../playwright/request_form_models/image01.py | 36 +++ tests/test_forms_single_model.py | 129 ++++++++++ .../test_request_form_models/__init__.py | 0 .../test_tutorial001.py | 232 +++++++++++++++++ .../test_tutorial001_an.py | 232 +++++++++++++++++ .../test_tutorial001_an_py39.py | 240 ++++++++++++++++++ 13 files changed, 994 insertions(+), 3 deletions(-) create mode 100644 docs/en/docs/img/tutorial/request-form-models/image01.png create mode 100644 docs/en/docs/tutorial/request-form-models.md create mode 100644 docs_src/request_form_models/tutorial001.py create mode 100644 docs_src/request_form_models/tutorial001_an.py create mode 100644 docs_src/request_form_models/tutorial001_an_py39.py create mode 100644 scripts/playwright/request_form_models/image01.py create mode 100644 tests/test_forms_single_model.py create mode 100644 tests/test_tutorial/test_request_form_models/__init__.py create mode 100644 tests/test_tutorial/test_request_form_models/test_tutorial001.py create mode 100644 tests/test_tutorial/test_request_form_models/test_tutorial001_an.py create mode 100644 tests/test_tutorial/test_request_form_models/test_tutorial001_an_py39.py diff --git a/docs/en/docs/img/tutorial/request-form-models/image01.png b/docs/en/docs/img/tutorial/request-form-models/image01.png new file mode 100644 index 0000000000000000000000000000000000000000..3fe32c03d589e76abec5e9c6b71374ebd4e8cd2c GIT binary patch literal 44487 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 CMaG{1 literal 0 HcmV?d00001 diff --git a/docs/en/docs/tutorial/request-form-models.md b/docs/en/docs/tutorial/request-form-models.md new file mode 100644 index 000000000..8bb1ffb1f --- /dev/null +++ b/docs/en/docs/tutorial/request-form-models.md @@ -0,0 +1,65 @@ +# Form Models + +You can use Pydantic models to declare form fields in FastAPI. + +/// info + +To use forms, first install <a href="https://github.com/Kludex/python-multipart" class="external-link" target="_blank">`python-multipart`</a>. + +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`: + +<div class="screenshot"> +<img src="/img/tutorial/request-form-models/image01.png"> +</div> diff --git a/docs/en/mkdocs.yml b/docs/en/mkdocs.yml index 528c80b8e..7c810c2d7 100644 --- a/docs/en/mkdocs.yml +++ b/docs/en/mkdocs.yml @@ -129,6 +129,7 @@ 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 new file mode 100644 index 000000000..98feff0b9 --- /dev/null +++ b/docs_src/request_form_models/tutorial001.py @@ -0,0 +1,14 @@ +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 new file mode 100644 index 000000000..30483d445 --- /dev/null +++ b/docs_src/request_form_models/tutorial001_an.py @@ -0,0 +1,15 @@ +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 new file mode 100644 index 000000000..7cc81aae9 --- /dev/null +++ b/docs_src/request_form_models/tutorial001_an_py39.py @@ -0,0 +1,16 @@ +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 7ac18d941..98ce17b55 100644 --- a/fastapi/dependencies/utils.py +++ b/fastapi/dependencies/utils.py @@ -33,6 +33,7 @@ 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, @@ -56,6 +57,7 @@ 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 @@ -743,7 +745,9 @@ 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): + if isinstance(first_field.field_info, params.Form) and not lenient_issubclass( + first_field.type_, BaseModel + ): return True return False @@ -783,7 +787,8 @@ 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) - values[field.name] = value + if value is not None: + values[field.name] = value return values @@ -798,8 +803,14 @@ 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(body_fields, received_body) + body_to_process = await _extract_form_body(fields_to_extract, 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 new file mode 100644 index 000000000..15bd3858c --- /dev/null +++ b/scripts/playwright/request_form_models/image01.py @@ -0,0 +1,36 @@ +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 new file mode 100644 index 000000000..7ed3ba3a2 --- /dev/null +++ b/tests/test_forms_single_model.py @@ -0,0 +1,129 @@ +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 new file mode 100644 index 000000000..e69de29bb diff --git a/tests/test_tutorial/test_request_form_models/test_tutorial001.py b/tests/test_tutorial/test_request_form_models/test_tutorial001.py new file mode 100644 index 000000000..46c130ee8 --- /dev/null +++ b/tests/test_tutorial/test_request_form_models/test_tutorial001.py @@ -0,0 +1,232 @@ +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 new file mode 100644 index 000000000..4e14d89c8 --- /dev/null +++ b/tests/test_tutorial/test_request_form_models/test_tutorial001_an.py @@ -0,0 +1,232 @@ +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 new file mode 100644 index 000000000..2e6426aa7 --- /dev/null +++ b/tests/test_tutorial/test_request_form_models/test_tutorial001_an_py39.py @@ -0,0 +1,240 @@ +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"}, + } + }, + }, + } + }, + }