diff --git a/docs/en/docs/tutorial/schema-extra-example.md b/docs/en/docs/tutorial/schema-extra-example.md
index 86ccb1f5a..39d184763 100644
--- a/docs/en/docs/tutorial/schema-extra-example.md
+++ b/docs/en/docs/tutorial/schema-extra-example.md
@@ -4,24 +4,48 @@ You can declare examples of the data your app can receive.
Here are several ways to do it.
-## Pydantic `schema_extra`
+## Extra JSON Schema data in Pydantic models
-You can declare `examples` for a Pydantic model using `Config` and `schema_extra`, as described in Pydantic's docs: Schema customization:
+You can declare `examples` for a Pydantic model that will be added to the generated JSON Schema.
-=== "Python 3.10+"
+=== "Python 3.10+ Pydantic v2"
- ```Python hl_lines="13-23"
+ ```Python hl_lines="13-24"
{!> ../../../docs_src/schema_extra_example/tutorial001_py310.py!}
```
-=== "Python 3.6+"
+=== "Python 3.10+ Pydantic v1"
- ```Python hl_lines="15-25"
+ ```Python hl_lines="13-23"
+ {!> ../../../docs_src/schema_extra_example/tutorial001_py310_pv1.py!}
+ ```
+
+=== "Python 3.6+ Pydantic v2"
+
+ ```Python hl_lines="15-26"
{!> ../../../docs_src/schema_extra_example/tutorial001.py!}
```
+=== "Python 3.6+ Pydantic v1"
+
+ ```Python hl_lines="15-25"
+ {!> ../../../docs_src/schema_extra_example/tutorial001_pv1.py!}
+ ```
+
That extra info will be added as-is to the output **JSON Schema** for that model, and it will be used in the API docs.
+=== "Pydantic v2"
+
+ In Pydantic version 2, you would use the attribute `model_config`, that takes a `dict` as described in Pydantic's docs: Model Config.
+
+ You can set `"json_schema_extra"` with a `dict` containing any additonal data you would like to show up in the generated JSON Schema, including `examples`.
+
+=== "Pydantic v1"
+
+ In Pydantic version 1, you would use an internal class `Config` and `schema_extra`, as described in Pydantic's docs: Schema customization.
+
+ You can set `schema_extra` with a `dict` containing any additonal data you would like to show up in the generated JSON Schema, including `examples`.
+
!!! tip
You could use the same technique to extend the JSON Schema and add your own custom extra info.
diff --git a/docs_src/schema_extra_example/tutorial001.py b/docs_src/schema_extra_example/tutorial001.py
index 6ab96ff85..32a66db3a 100644
--- a/docs_src/schema_extra_example/tutorial001.py
+++ b/docs_src/schema_extra_example/tutorial001.py
@@ -12,8 +12,8 @@ class Item(BaseModel):
price: float
tax: Union[float, None] = None
- class Config:
- schema_extra = {
+ model_config = {
+ "json_schema_extra": {
"examples": [
{
"name": "Foo",
@@ -23,6 +23,7 @@ class Item(BaseModel):
}
]
}
+ }
@app.put("/items/{item_id}")
diff --git a/docs_src/schema_extra_example/tutorial001_pv1.py b/docs_src/schema_extra_example/tutorial001_pv1.py
new file mode 100644
index 000000000..6ab96ff85
--- /dev/null
+++ b/docs_src/schema_extra_example/tutorial001_pv1.py
@@ -0,0 +1,31 @@
+from typing import Union
+
+from fastapi import FastAPI
+from pydantic import BaseModel
+
+app = FastAPI()
+
+
+class Item(BaseModel):
+ name: str
+ description: Union[str, None] = None
+ price: float
+ tax: Union[float, None] = None
+
+ class Config:
+ schema_extra = {
+ "examples": [
+ {
+ "name": "Foo",
+ "description": "A very nice Item",
+ "price": 35.4,
+ "tax": 3.2,
+ }
+ ]
+ }
+
+
+@app.put("/items/{item_id}")
+async def update_item(item_id: int, item: Item):
+ results = {"item_id": item_id, "item": item}
+ return results
diff --git a/docs_src/schema_extra_example/tutorial001_py310.py b/docs_src/schema_extra_example/tutorial001_py310.py
index ec83f1112..84aa5fc12 100644
--- a/docs_src/schema_extra_example/tutorial001_py310.py
+++ b/docs_src/schema_extra_example/tutorial001_py310.py
@@ -10,8 +10,8 @@ class Item(BaseModel):
price: float
tax: float | None = None
- class Config:
- schema_extra = {
+ model_config = {
+ "json_schema_extra": {
"examples": [
{
"name": "Foo",
@@ -21,6 +21,7 @@ class Item(BaseModel):
}
]
}
+ }
@app.put("/items/{item_id}")
diff --git a/docs_src/schema_extra_example/tutorial001_py310_pv1.py b/docs_src/schema_extra_example/tutorial001_py310_pv1.py
new file mode 100644
index 000000000..ec83f1112
--- /dev/null
+++ b/docs_src/schema_extra_example/tutorial001_py310_pv1.py
@@ -0,0 +1,29 @@
+from fastapi import FastAPI
+from pydantic import BaseModel
+
+app = FastAPI()
+
+
+class Item(BaseModel):
+ name: str
+ description: str | None = None
+ price: float
+ tax: float | None = None
+
+ class Config:
+ schema_extra = {
+ "examples": [
+ {
+ "name": "Foo",
+ "description": "A very nice Item",
+ "price": 35.4,
+ "tax": 3.2,
+ }
+ ]
+ }
+
+
+@app.put("/items/{item_id}")
+async def update_item(item_id: int, item: Item):
+ results = {"item_id": item_id, "item": item}
+ return results
diff --git a/tests/test_tutorial/test_schema_extra_example/test_tutorial001.py b/tests/test_tutorial/test_schema_extra_example/test_tutorial001.py
new file mode 100644
index 000000000..98b187355
--- /dev/null
+++ b/tests/test_tutorial/test_schema_extra_example/test_tutorial001.py
@@ -0,0 +1,133 @@
+import pytest
+from fastapi.testclient import TestClient
+
+from ...utils import needs_pydanticv2
+
+
+@pytest.fixture(name="client")
+def get_client():
+ from docs_src.schema_extra_example.tutorial001 import app
+
+ client = TestClient(app)
+ return client
+
+
+@needs_pydanticv2
+def test_post_body_example(client: TestClient):
+ response = client.put(
+ "/items/5",
+ json={
+ "name": "Foo",
+ "description": "A very nice Item",
+ "price": 35.4,
+ "tax": 3.2,
+ },
+ )
+ assert response.status_code == 200
+
+
+@needs_pydanticv2
+def test_openapi_schema(client: TestClient):
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ # insert_assert(response.json())
+ assert response.json() == {
+ "openapi": "3.1.0",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/items/{item_id}": {
+ "put": {
+ "summary": "Update Item",
+ "operationId": "update_item_items__item_id__put",
+ "parameters": [
+ {
+ "name": "item_id",
+ "in": "path",
+ "required": True,
+ "schema": {"type": "integer", "title": "Item Id"},
+ }
+ ],
+ "requestBody": {
+ "required": True,
+ "content": {
+ "application/json": {
+ "schema": {"$ref": "#/components/schemas/Item"}
+ }
+ },
+ },
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {"application/json": {"schema": {}}},
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ }
+ }
+ },
+ "components": {
+ "schemas": {
+ "HTTPValidationError": {
+ "properties": {
+ "detail": {
+ "items": {"$ref": "#/components/schemas/ValidationError"},
+ "type": "array",
+ "title": "Detail",
+ }
+ },
+ "type": "object",
+ "title": "HTTPValidationError",
+ },
+ "Item": {
+ "properties": {
+ "name": {"type": "string", "title": "Name"},
+ "description": {
+ "anyOf": [{"type": "string"}, {"type": "null"}],
+ "title": "Description",
+ },
+ "price": {"type": "number", "title": "Price"},
+ "tax": {
+ "anyOf": [{"type": "number"}, {"type": "null"}],
+ "title": "Tax",
+ },
+ },
+ "type": "object",
+ "required": ["name", "price"],
+ "title": "Item",
+ "examples": [
+ {
+ "description": "A very nice Item",
+ "name": "Foo",
+ "price": 35.4,
+ "tax": 3.2,
+ }
+ ],
+ },
+ "ValidationError": {
+ "properties": {
+ "loc": {
+ "items": {
+ "anyOf": [{"type": "string"}, {"type": "integer"}]
+ },
+ "type": "array",
+ "title": "Location",
+ },
+ "msg": {"type": "string", "title": "Message"},
+ "type": {"type": "string", "title": "Error Type"},
+ },
+ "type": "object",
+ "required": ["loc", "msg", "type"],
+ "title": "ValidationError",
+ },
+ }
+ },
+ }
diff --git a/tests/test_tutorial/test_schema_extra_example/test_tutorial001_pv1.py b/tests/test_tutorial/test_schema_extra_example/test_tutorial001_pv1.py
new file mode 100644
index 000000000..3520ef61d
--- /dev/null
+++ b/tests/test_tutorial/test_schema_extra_example/test_tutorial001_pv1.py
@@ -0,0 +1,127 @@
+import pytest
+from fastapi.testclient import TestClient
+
+from ...utils import needs_pydanticv1
+
+
+@pytest.fixture(name="client")
+def get_client():
+ from docs_src.schema_extra_example.tutorial001_pv1 import app
+
+ client = TestClient(app)
+ return client
+
+
+@needs_pydanticv1
+def test_post_body_example(client: TestClient):
+ response = client.put(
+ "/items/5",
+ json={
+ "name": "Foo",
+ "description": "A very nice Item",
+ "price": 35.4,
+ "tax": 3.2,
+ },
+ )
+ assert response.status_code == 200
+
+
+@needs_pydanticv1
+def test_openapi_schema(client: TestClient):
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ # insert_assert(response.json())
+ assert response.json() == {
+ "openapi": "3.1.0",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/items/{item_id}": {
+ "put": {
+ "summary": "Update Item",
+ "operationId": "update_item_items__item_id__put",
+ "parameters": [
+ {
+ "required": True,
+ "schema": {"type": "integer", "title": "Item Id"},
+ "name": "item_id",
+ "in": "path",
+ }
+ ],
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {"$ref": "#/components/schemas/Item"}
+ }
+ },
+ "required": True,
+ },
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {"application/json": {"schema": {}}},
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ }
+ }
+ },
+ "components": {
+ "schemas": {
+ "HTTPValidationError": {
+ "properties": {
+ "detail": {
+ "items": {"$ref": "#/components/schemas/ValidationError"},
+ "type": "array",
+ "title": "Detail",
+ }
+ },
+ "type": "object",
+ "title": "HTTPValidationError",
+ },
+ "Item": {
+ "properties": {
+ "name": {"type": "string", "title": "Name"},
+ "description": {"type": "string", "title": "Description"},
+ "price": {"type": "number", "title": "Price"},
+ "tax": {"type": "number", "title": "Tax"},
+ },
+ "type": "object",
+ "required": ["name", "price"],
+ "title": "Item",
+ "examples": [
+ {
+ "name": "Foo",
+ "description": "A very nice Item",
+ "price": 35.4,
+ "tax": 3.2,
+ }
+ ],
+ },
+ "ValidationError": {
+ "properties": {
+ "loc": {
+ "items": {
+ "anyOf": [{"type": "string"}, {"type": "integer"}]
+ },
+ "type": "array",
+ "title": "Location",
+ },
+ "msg": {"type": "string", "title": "Message"},
+ "type": {"type": "string", "title": "Error Type"},
+ },
+ "type": "object",
+ "required": ["loc", "msg", "type"],
+ "title": "ValidationError",
+ },
+ }
+ },
+ }
diff --git a/tests/test_tutorial/test_schema_extra_example/test_tutorial001_py310.py b/tests/test_tutorial/test_schema_extra_example/test_tutorial001_py310.py
new file mode 100644
index 000000000..e63e33cda
--- /dev/null
+++ b/tests/test_tutorial/test_schema_extra_example/test_tutorial001_py310.py
@@ -0,0 +1,135 @@
+import pytest
+from fastapi.testclient import TestClient
+
+from ...utils import needs_py310, needs_pydanticv2
+
+
+@pytest.fixture(name="client")
+def get_client():
+ from docs_src.schema_extra_example.tutorial001_py310 import app
+
+ client = TestClient(app)
+ return client
+
+
+@needs_py310
+@needs_pydanticv2
+def test_post_body_example(client: TestClient):
+ response = client.put(
+ "/items/5",
+ json={
+ "name": "Foo",
+ "description": "A very nice Item",
+ "price": 35.4,
+ "tax": 3.2,
+ },
+ )
+ assert response.status_code == 200
+
+
+@needs_py310
+@needs_pydanticv2
+def test_openapi_schema(client: TestClient):
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ # insert_assert(response.json())
+ assert response.json() == {
+ "openapi": "3.1.0",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/items/{item_id}": {
+ "put": {
+ "summary": "Update Item",
+ "operationId": "update_item_items__item_id__put",
+ "parameters": [
+ {
+ "name": "item_id",
+ "in": "path",
+ "required": True,
+ "schema": {"type": "integer", "title": "Item Id"},
+ }
+ ],
+ "requestBody": {
+ "required": True,
+ "content": {
+ "application/json": {
+ "schema": {"$ref": "#/components/schemas/Item"}
+ }
+ },
+ },
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {"application/json": {"schema": {}}},
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ }
+ }
+ },
+ "components": {
+ "schemas": {
+ "HTTPValidationError": {
+ "properties": {
+ "detail": {
+ "items": {"$ref": "#/components/schemas/ValidationError"},
+ "type": "array",
+ "title": "Detail",
+ }
+ },
+ "type": "object",
+ "title": "HTTPValidationError",
+ },
+ "Item": {
+ "properties": {
+ "name": {"type": "string", "title": "Name"},
+ "description": {
+ "anyOf": [{"type": "string"}, {"type": "null"}],
+ "title": "Description",
+ },
+ "price": {"type": "number", "title": "Price"},
+ "tax": {
+ "anyOf": [{"type": "number"}, {"type": "null"}],
+ "title": "Tax",
+ },
+ },
+ "type": "object",
+ "required": ["name", "price"],
+ "title": "Item",
+ "examples": [
+ {
+ "description": "A very nice Item",
+ "name": "Foo",
+ "price": 35.4,
+ "tax": 3.2,
+ }
+ ],
+ },
+ "ValidationError": {
+ "properties": {
+ "loc": {
+ "items": {
+ "anyOf": [{"type": "string"}, {"type": "integer"}]
+ },
+ "type": "array",
+ "title": "Location",
+ },
+ "msg": {"type": "string", "title": "Message"},
+ "type": {"type": "string", "title": "Error Type"},
+ },
+ "type": "object",
+ "required": ["loc", "msg", "type"],
+ "title": "ValidationError",
+ },
+ }
+ },
+ }
diff --git a/tests/test_tutorial/test_schema_extra_example/test_tutorial001_py310_pv1.py b/tests/test_tutorial/test_schema_extra_example/test_tutorial001_py310_pv1.py
new file mode 100644
index 000000000..e036d6b68
--- /dev/null
+++ b/tests/test_tutorial/test_schema_extra_example/test_tutorial001_py310_pv1.py
@@ -0,0 +1,129 @@
+import pytest
+from fastapi.testclient import TestClient
+
+from ...utils import needs_py310, needs_pydanticv1
+
+
+@pytest.fixture(name="client")
+def get_client():
+ from docs_src.schema_extra_example.tutorial001_py310_pv1 import app
+
+ client = TestClient(app)
+ return client
+
+
+@needs_py310
+@needs_pydanticv1
+def test_post_body_example(client: TestClient):
+ response = client.put(
+ "/items/5",
+ json={
+ "name": "Foo",
+ "description": "A very nice Item",
+ "price": 35.4,
+ "tax": 3.2,
+ },
+ )
+ assert response.status_code == 200
+
+
+@needs_py310
+@needs_pydanticv1
+def test_openapi_schema(client: TestClient):
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ # insert_assert(response.json())
+ assert response.json() == {
+ "openapi": "3.1.0",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/items/{item_id}": {
+ "put": {
+ "summary": "Update Item",
+ "operationId": "update_item_items__item_id__put",
+ "parameters": [
+ {
+ "required": True,
+ "schema": {"type": "integer", "title": "Item Id"},
+ "name": "item_id",
+ "in": "path",
+ }
+ ],
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {"$ref": "#/components/schemas/Item"}
+ }
+ },
+ "required": True,
+ },
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {"application/json": {"schema": {}}},
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ }
+ }
+ },
+ "components": {
+ "schemas": {
+ "HTTPValidationError": {
+ "properties": {
+ "detail": {
+ "items": {"$ref": "#/components/schemas/ValidationError"},
+ "type": "array",
+ "title": "Detail",
+ }
+ },
+ "type": "object",
+ "title": "HTTPValidationError",
+ },
+ "Item": {
+ "properties": {
+ "name": {"type": "string", "title": "Name"},
+ "description": {"type": "string", "title": "Description"},
+ "price": {"type": "number", "title": "Price"},
+ "tax": {"type": "number", "title": "Tax"},
+ },
+ "type": "object",
+ "required": ["name", "price"],
+ "title": "Item",
+ "examples": [
+ {
+ "name": "Foo",
+ "description": "A very nice Item",
+ "price": 35.4,
+ "tax": 3.2,
+ }
+ ],
+ },
+ "ValidationError": {
+ "properties": {
+ "loc": {
+ "items": {
+ "anyOf": [{"type": "string"}, {"type": "integer"}]
+ },
+ "type": "array",
+ "title": "Location",
+ },
+ "msg": {"type": "string", "title": "Message"},
+ "type": {"type": "string", "title": "Error Type"},
+ },
+ "type": "object",
+ "required": ["loc", "msg", "type"],
+ "title": "ValidationError",
+ },
+ }
+ },
+ }