mirror of https://github.com/tiangolo/fastapi.git
📝 Add docs for Pydantic v2 for `docs/en/docs/advanced/path-operation-advanced-configuration.md` (#9798)
This commit is contained in:
parent
00e080ab42
commit
ef5e17fcd6
|
|
@ -150,9 +150,20 @@ And you could do this even if the data type in the request is not JSON.
|
|||
|
||||
For example, in this application we don't use FastAPI's integrated functionality to extract the JSON Schema from Pydantic models nor the automatic validation for JSON. In fact, we are declaring the request content type as YAML, not JSON:
|
||||
|
||||
```Python hl_lines="17-22 24"
|
||||
{!../../../docs_src/path_operation_advanced_configuration/tutorial007.py!}
|
||||
```
|
||||
=== "Pydantic v2"
|
||||
|
||||
```Python hl_lines="17-22 24"
|
||||
{!> ../../../docs_src/path_operation_advanced_configuration/tutorial007.py!}
|
||||
```
|
||||
|
||||
=== "Pydantic v1"
|
||||
|
||||
```Python hl_lines="17-22 24"
|
||||
{!> ../../../docs_src/path_operation_advanced_configuration/tutorial007_pv1.py!}
|
||||
```
|
||||
|
||||
!!! info
|
||||
In Pydantic version 1 the method to get the JSON Schema for a model was called `Item.schema()`, in Pydantic version 2, the method is called `Item.model_schema_json()`.
|
||||
|
||||
Nevertheless, although we are not using the default integrated functionality, we are still using a Pydantic model to manually generate the JSON Schema for the data that we want to receive in YAML.
|
||||
|
||||
|
|
@ -160,9 +171,20 @@ Then we use the request directly, and extract the body as `bytes`. This means th
|
|||
|
||||
And then in our code, we parse that YAML content directly, and then we are again using the same Pydantic model to validate the YAML content:
|
||||
|
||||
```Python hl_lines="26-33"
|
||||
{!../../../docs_src/path_operation_advanced_configuration/tutorial007.py!}
|
||||
```
|
||||
=== "Pydantic v2"
|
||||
|
||||
```Python hl_lines="26-33"
|
||||
{!> ../../../docs_src/path_operation_advanced_configuration/tutorial007.py!}
|
||||
```
|
||||
|
||||
=== "Pydantic v1"
|
||||
|
||||
```Python hl_lines="26-33"
|
||||
{!> ../../../docs_src/path_operation_advanced_configuration/tutorial007_pv1.py!}
|
||||
```
|
||||
|
||||
!!! info
|
||||
In Pydantic version 1 the method to parse and validate an object was `Item.parse_obj()`, in Pydantic version 2, the method is called `Item.model_validate()`.
|
||||
|
||||
!!! tip
|
||||
Here we re-use the same Pydantic model.
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ class Item(BaseModel):
|
|||
"/items/",
|
||||
openapi_extra={
|
||||
"requestBody": {
|
||||
"content": {"application/x-yaml": {"schema": Item.schema()}},
|
||||
"content": {"application/x-yaml": {"schema": Item.model_json_schema()}},
|
||||
"required": True,
|
||||
},
|
||||
},
|
||||
|
|
@ -28,7 +28,7 @@ async def create_item(request: Request):
|
|||
except yaml.YAMLError:
|
||||
raise HTTPException(status_code=422, detail="Invalid YAML")
|
||||
try:
|
||||
item = Item.parse_obj(data)
|
||||
item = Item.model_validate(data)
|
||||
except ValidationError as e:
|
||||
raise HTTPException(status_code=422, detail=e.errors())
|
||||
return item
|
||||
|
|
|
|||
|
|
@ -0,0 +1,34 @@
|
|||
from typing import List
|
||||
|
||||
import yaml
|
||||
from fastapi import FastAPI, HTTPException, Request
|
||||
from pydantic import BaseModel, ValidationError
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class Item(BaseModel):
|
||||
name: str
|
||||
tags: List[str]
|
||||
|
||||
|
||||
@app.post(
|
||||
"/items/",
|
||||
openapi_extra={
|
||||
"requestBody": {
|
||||
"content": {"application/x-yaml": {"schema": Item.schema()}},
|
||||
"required": True,
|
||||
},
|
||||
},
|
||||
)
|
||||
async def create_item(request: Request):
|
||||
raw_body = await request.body()
|
||||
try:
|
||||
data = yaml.safe_load(raw_body)
|
||||
except yaml.YAMLError:
|
||||
raise HTTPException(status_code=422, detail="Invalid YAML")
|
||||
try:
|
||||
item = Item.parse_obj(data)
|
||||
except ValidationError as e:
|
||||
raise HTTPException(status_code=422, detail=e.errors())
|
||||
return item
|
||||
|
|
@ -146,6 +146,7 @@ ignore = [
|
|||
"docs_src/custom_response/tutorial007.py" = ["B007"]
|
||||
"docs_src/dataclasses/tutorial003.py" = ["I001"]
|
||||
"docs_src/path_operation_advanced_configuration/tutorial007.py" = ["B904"]
|
||||
"docs_src/path_operation_advanced_configuration/tutorial007_pv1.py" = ["B904"]
|
||||
"docs_src/custom_request_and_route/tutorial002.py" = ["B904"]
|
||||
"docs_src/dependencies/tutorial008_an.py" = ["F821"]
|
||||
"docs_src/dependencies/tutorial008_an_py39.py" = ["F821"]
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
from fastapi.utils import match_pydantic_error_url
|
||||
|
||||
from ...utils import needs_pydanticv1
|
||||
from ...utils import needs_pydanticv2
|
||||
|
||||
|
||||
@pytest.fixture(name="client")
|
||||
|
|
@ -12,8 +13,7 @@ def get_client():
|
|||
return client
|
||||
|
||||
|
||||
# TODO: pv2 add Pydantic v2 version
|
||||
@needs_pydanticv1
|
||||
@needs_pydanticv2
|
||||
def test_post(client: TestClient):
|
||||
yaml_data = """
|
||||
name: Deadpoolio
|
||||
|
|
@ -30,8 +30,7 @@ def test_post(client: TestClient):
|
|||
}
|
||||
|
||||
|
||||
# TODO: pv2 add Pydantic v2 version
|
||||
@needs_pydanticv1
|
||||
@needs_pydanticv2
|
||||
def test_post_broken_yaml(client: TestClient):
|
||||
yaml_data = """
|
||||
name: Deadpoolio
|
||||
|
|
@ -45,8 +44,7 @@ def test_post_broken_yaml(client: TestClient):
|
|||
assert response.json() == {"detail": "Invalid YAML"}
|
||||
|
||||
|
||||
# TODO: pv2 add Pydantic v2 version
|
||||
@needs_pydanticv1
|
||||
@needs_pydanticv2
|
||||
def test_post_invalid(client: TestClient):
|
||||
yaml_data = """
|
||||
name: Deadpoolio
|
||||
|
|
@ -58,15 +56,21 @@ def test_post_invalid(client: TestClient):
|
|||
"""
|
||||
response = client.post("/items/", content=yaml_data)
|
||||
assert response.status_code == 422, response.text
|
||||
# insert_assert(response.json())
|
||||
assert response.json() == {
|
||||
"detail": [
|
||||
{"loc": ["tags", 3], "msg": "str type expected", "type": "type_error.str"}
|
||||
{
|
||||
"type": "string_type",
|
||||
"loc": ["tags", 3],
|
||||
"msg": "Input should be a valid string",
|
||||
"input": {"sneaky": "object"},
|
||||
"url": match_pydantic_error_url("string_type"),
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
# TODO: pv2 add Pydantic v2 version
|
||||
@needs_pydanticv1
|
||||
@needs_pydanticv2
|
||||
def test_openapi_schema(client: TestClient):
|
||||
response = client.get("/openapi.json")
|
||||
assert response.status_code == 200, response.text
|
||||
|
|
|
|||
|
|
@ -0,0 +1,106 @@
|
|||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from ...utils import needs_pydanticv1
|
||||
|
||||
|
||||
@pytest.fixture(name="client")
|
||||
def get_client():
|
||||
from docs_src.path_operation_advanced_configuration.tutorial007_pv1 import app
|
||||
|
||||
client = TestClient(app)
|
||||
return client
|
||||
|
||||
|
||||
@needs_pydanticv1
|
||||
def test_post(client: TestClient):
|
||||
yaml_data = """
|
||||
name: Deadpoolio
|
||||
tags:
|
||||
- x-force
|
||||
- x-men
|
||||
- x-avengers
|
||||
"""
|
||||
response = client.post("/items/", content=yaml_data)
|
||||
assert response.status_code == 200, response.text
|
||||
assert response.json() == {
|
||||
"name": "Deadpoolio",
|
||||
"tags": ["x-force", "x-men", "x-avengers"],
|
||||
}
|
||||
|
||||
|
||||
@needs_pydanticv1
|
||||
def test_post_broken_yaml(client: TestClient):
|
||||
yaml_data = """
|
||||
name: Deadpoolio
|
||||
tags:
|
||||
x - x-force
|
||||
x - x-men
|
||||
x - x-avengers
|
||||
"""
|
||||
response = client.post("/items/", content=yaml_data)
|
||||
assert response.status_code == 422, response.text
|
||||
assert response.json() == {"detail": "Invalid YAML"}
|
||||
|
||||
|
||||
@needs_pydanticv1
|
||||
def test_post_invalid(client: TestClient):
|
||||
yaml_data = """
|
||||
name: Deadpoolio
|
||||
tags:
|
||||
- x-force
|
||||
- x-men
|
||||
- x-avengers
|
||||
- sneaky: object
|
||||
"""
|
||||
response = client.post("/items/", content=yaml_data)
|
||||
assert response.status_code == 422, response.text
|
||||
assert response.json() == {
|
||||
"detail": [
|
||||
{"loc": ["tags", 3], "msg": "str type expected", "type": "type_error.str"}
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@needs_pydanticv1
|
||||
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": {
|
||||
"/items/": {
|
||||
"post": {
|
||||
"summary": "Create Item",
|
||||
"operationId": "create_item_items__post",
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/x-yaml": {
|
||||
"schema": {
|
||||
"title": "Item",
|
||||
"required": ["name", "tags"],
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {"title": "Name", "type": "string"},
|
||||
"tags": {
|
||||
"title": "Tags",
|
||||
"type": "array",
|
||||
"items": {"type": "string"},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": True,
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {"application/json": {"schema": {}}},
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
Loading…
Reference in New Issue