154 changed files with 2308 additions and 4311 deletions
@ -22,22 +22,13 @@ Here's a general idea of how the models could look like with their password fiel |
|||||
|
|
||||
{* ../../docs_src/extra_models/tutorial001_py310.py hl[7,9,14,20,22,27:28,31:33,38:39] *} |
{* ../../docs_src/extra_models/tutorial001_py310.py hl[7,9,14,20,22,27:28,31:33,38:39] *} |
||||
|
|
||||
|
### About `**user_in.model_dump()` { #about-user-in-model-dump } |
||||
|
|
||||
/// info |
#### Pydantic's `.model_dump()` { #pydantics-model-dump } |
||||
|
|
||||
In Pydantic v1 the method was called `.dict()`, it was deprecated (but still supported) in Pydantic v2, and renamed to `.model_dump()`. |
|
||||
|
|
||||
The examples here use `.dict()` for compatibility with Pydantic v1, but you should use `.model_dump()` instead if you can use Pydantic v2. |
|
||||
|
|
||||
/// |
|
||||
|
|
||||
### About `**user_in.dict()` { #about-user-in-dict } |
|
||||
|
|
||||
#### Pydantic's `.dict()` { #pydantics-dict } |
|
||||
|
|
||||
`user_in` is a Pydantic model of class `UserIn`. |
`user_in` is a Pydantic model of class `UserIn`. |
||||
|
|
||||
Pydantic models have a `.dict()` method that returns a `dict` with the model's data. |
Pydantic models have a `.model_dump()` method that returns a `dict` with the model's data. |
||||
|
|
||||
So, if we create a Pydantic object `user_in` like: |
So, if we create a Pydantic object `user_in` like: |
||||
|
|
||||
@ -48,7 +39,7 @@ user_in = UserIn(username="john", password="secret", email="[email protected] |
|||||
and then we call: |
and then we call: |
||||
|
|
||||
```Python |
```Python |
||||
user_dict = user_in.dict() |
user_dict = user_in.model_dump() |
||||
``` |
``` |
||||
|
|
||||
we now have a `dict` with the data in the variable `user_dict` (it's a `dict` instead of a Pydantic model object). |
we now have a `dict` with the data in the variable `user_dict` (it's a `dict` instead of a Pydantic model object). |
||||
@ -104,20 +95,20 @@ UserInDB( |
|||||
|
|
||||
#### A Pydantic model from the contents of another { #a-pydantic-model-from-the-contents-of-another } |
#### A Pydantic model from the contents of another { #a-pydantic-model-from-the-contents-of-another } |
||||
|
|
||||
As in the example above we got `user_dict` from `user_in.dict()`, this code: |
As in the example above we got `user_dict` from `user_in.model_dump()`, this code: |
||||
|
|
||||
```Python |
```Python |
||||
user_dict = user_in.dict() |
user_dict = user_in.model_dump() |
||||
UserInDB(**user_dict) |
UserInDB(**user_dict) |
||||
``` |
``` |
||||
|
|
||||
would be equivalent to: |
would be equivalent to: |
||||
|
|
||||
```Python |
```Python |
||||
UserInDB(**user_in.dict()) |
UserInDB(**user_in.model_dump()) |
||||
``` |
``` |
||||
|
|
||||
...because `user_in.dict()` is a `dict`, and then we make Python "unpack" it by passing it to `UserInDB` prefixed with `**`. |
...because `user_in.model_dump()` is a `dict`, and then we make Python "unpack" it by passing it to `UserInDB` prefixed with `**`. |
||||
|
|
||||
So, we get a Pydantic model from the data in another Pydantic model. |
So, we get a Pydantic model from the data in another Pydantic model. |
||||
|
|
||||
@ -126,7 +117,7 @@ So, we get a Pydantic model from the data in another Pydantic model. |
|||||
And then adding the extra keyword argument `hashed_password=hashed_password`, like in: |
And then adding the extra keyword argument `hashed_password=hashed_password`, like in: |
||||
|
|
||||
```Python |
```Python |
||||
UserInDB(**user_in.dict(), hashed_password=hashed_password) |
UserInDB(**user_in.model_dump(), hashed_password=hashed_password) |
||||
``` |
``` |
||||
|
|
||||
...ends up being like: |
...ends up being like: |
||||
@ -181,7 +172,6 @@ When defining a <a href="https://docs.pydantic.dev/latest/concepts/types/#unions |
|||||
|
|
||||
{* ../../docs_src/extra_models/tutorial003_py310.py hl[1,14:15,18:20,33] *} |
{* ../../docs_src/extra_models/tutorial003_py310.py hl[1,14:15,18:20,33] *} |
||||
|
|
||||
|
|
||||
### `Union` in Python 3.10 { #union-in-python-3-10 } |
### `Union` in Python 3.10 { #union-in-python-3-10 } |
||||
|
|
||||
In this example we pass `Union[PlaneItem, CarItem]` as the value of the argument `response_model`. |
In this example we pass `Union[PlaneItem, CarItem]` as the value of the argument `response_model`. |
||||
@ -204,7 +194,6 @@ For that, use the standard Python `typing.List` (or just `list` in Python 3.9 an |
|||||
|
|
||||
{* ../../docs_src/extra_models/tutorial004_py39.py hl[18] *} |
{* ../../docs_src/extra_models/tutorial004_py39.py hl[18] *} |
||||
|
|
||||
|
|
||||
## Response with arbitrary `dict` { #response-with-arbitrary-dict } |
## Response with arbitrary `dict` { #response-with-arbitrary-dict } |
||||
|
|
||||
You can also declare a response using a plain arbitrary `dict`, declaring just the type of the keys and values, without using a Pydantic model. |
You can also declare a response using a plain arbitrary `dict`, declaring just the type of the keys and values, without using a Pydantic model. |
||||
@ -215,7 +204,6 @@ In this case, you can use `typing.Dict` (or just `dict` in Python 3.9 and above) |
|||||
|
|
||||
{* ../../docs_src/extra_models/tutorial005_py39.py hl[6] *} |
{* ../../docs_src/extra_models/tutorial005_py39.py hl[6] *} |
||||
|
|
||||
|
|
||||
## Recap { #recap } |
## Recap { #recap } |
||||
|
|
||||
Use multiple Pydantic models and inherit freely for each case. |
Use multiple Pydantic models and inherit freely for each case. |
||||
|
|||||
@ -0,0 +1,47 @@ |
|||||
|
### Target language |
||||
|
|
||||
|
Translate to Japanese (日本語). |
||||
|
|
||||
|
Language code: ja. |
||||
|
|
||||
|
### Grammar and tone |
||||
|
|
||||
|
1) Use polite, instructional Japanese (です/ます調). |
||||
|
2) Keep the tone concise and technical (match existing Japanese FastAPI docs). |
||||
|
|
||||
|
### Headings |
||||
|
|
||||
|
1) Follow the existing Japanese style: short, descriptive headings (often noun phrases), e.g. 「チェック」. |
||||
|
2) Do not add a trailing period at the end of headings. |
||||
|
|
||||
|
### Quotes |
||||
|
|
||||
|
1) Prefer Japanese corner brackets 「」 in normal prose when quoting a term. |
||||
|
2) Do not change quotes inside inline code, code blocks, URLs, or file paths. |
||||
|
|
||||
|
### Ellipsis |
||||
|
|
||||
|
1) Keep ellipsis style consistent with existing Japanese docs (commonly `...`). |
||||
|
2) Never change `...` in code, URLs, or CLI examples. |
||||
|
|
||||
|
### Preferred translations / glossary |
||||
|
|
||||
|
Use the following preferred translations when they apply in documentation prose: |
||||
|
|
||||
|
- request (HTTP): リクエスト |
||||
|
- response (HTTP): レスポンス |
||||
|
- path operation: パスオペレーション |
||||
|
- path operation function: パスオペレーション関数 |
||||
|
|
||||
|
### `///` admonitions |
||||
|
|
||||
|
1) Keep the admonition keyword in English (do not translate `note`, `tip`, etc.). |
||||
|
2) If a title is present, prefer these canonical titles: |
||||
|
|
||||
|
- `/// note | 備考` |
||||
|
- `/// note | 技術詳細` |
||||
|
- `/// tip | 豆知識` |
||||
|
- `/// warning | 注意` |
||||
|
- `/// info | 情報` |
||||
|
- `/// check | 確認` |
||||
|
- `/// danger | 警告` |
||||
@ -0,0 +1,51 @@ |
|||||
|
### Target language |
||||
|
|
||||
|
Translate to Korean (한국어). |
||||
|
|
||||
|
Language code: ko. |
||||
|
|
||||
|
### Grammar and tone |
||||
|
|
||||
|
1) Use polite, instructional Korean (e.g. 합니다/하세요 style). |
||||
|
2) Keep the tone consistent with the existing Korean FastAPI docs. |
||||
|
|
||||
|
### Headings |
||||
|
|
||||
|
1) Follow existing Korean heading style (short, action-oriented headings like “확인하기”). |
||||
|
2) Do not add trailing punctuation to headings. |
||||
|
|
||||
|
### Quotes |
||||
|
|
||||
|
1) Keep quote style consistent with the existing Korean docs. |
||||
|
2) Never change quotes inside inline code, code blocks, URLs, or file paths. |
||||
|
|
||||
|
### Ellipsis |
||||
|
|
||||
|
1) Keep ellipsis style consistent with existing Korean docs (often `...`). |
||||
|
2) Never change `...` in code, URLs, or CLI examples. |
||||
|
|
||||
|
### Preferred translations / glossary |
||||
|
|
||||
|
Use the following preferred translations when they apply in documentation prose: |
||||
|
|
||||
|
- request (HTTP): 요청 |
||||
|
- response (HTTP): 응답 |
||||
|
- path operation: 경로 처리 |
||||
|
- path operation function: 경로 처리 함수 |
||||
|
|
||||
|
### `///` admonitions |
||||
|
|
||||
|
1) Keep the admonition keyword in English (do not translate `note`, `tip`, etc.). |
||||
|
2) If a title is present, prefer these canonical titles: |
||||
|
|
||||
|
- `/// note | 참고` |
||||
|
- `/// tip | 팁` |
||||
|
- `/// warning | 경고` |
||||
|
- `/// info | 정보` |
||||
|
- `/// danger | 위험` |
||||
|
- `/// note Technical Details | 기술 세부사항` |
||||
|
- `/// check | 확인` |
||||
|
Notes: |
||||
|
|
||||
|
- `details` blocks exist in Korean docs; keep `/// details` as-is and translate only the title after `|`. |
||||
|
- Example canonical title used: `/// details | 상세 설명` |
||||
@ -0,0 +1,46 @@ |
|||||
|
### Target language |
||||
|
|
||||
|
Translate to Ukrainian (українська). |
||||
|
|
||||
|
Language code: uk. |
||||
|
|
||||
|
### Grammar and tone |
||||
|
|
||||
|
1) Use polite/formal address consistent with existing Ukrainian docs (use “ви/ваш”). |
||||
|
2) Keep the tone concise and technical. |
||||
|
|
||||
|
### Headings |
||||
|
|
||||
|
1) Follow existing Ukrainian heading style; keep headings short and instructional. |
||||
|
2) Do not add trailing punctuation to headings. |
||||
|
|
||||
|
### Quotes |
||||
|
|
||||
|
1) Prefer Ukrainian guillemets «…» for quoted terms in prose, matching existing Ukrainian docs. |
||||
|
2) Never change quotes inside inline code, code blocks, URLs, or file paths. |
||||
|
|
||||
|
### Ellipsis |
||||
|
|
||||
|
1) Keep ellipsis style consistent with existing Ukrainian docs. |
||||
|
2) Never change `...` in code, URLs, or CLI examples. |
||||
|
|
||||
|
### Preferred translations / glossary |
||||
|
|
||||
|
Use the following preferred translations when they apply in documentation prose: |
||||
|
|
||||
|
- request (HTTP): запит |
||||
|
- response (HTTP): відповідь |
||||
|
- path operation: операція шляху |
||||
|
- path operation function: функція операції шляху |
||||
|
|
||||
|
### `///` admonitions |
||||
|
|
||||
|
1) Keep the admonition keyword in English (do not translate `note`, `tip`, etc.). |
||||
|
2) If a title is present, prefer these canonical titles (choose one canonical form where variants exist): |
||||
|
|
||||
|
- `/// note | Примітка` |
||||
|
- `/// note | Технічні деталі` |
||||
|
- `/// tip | Порада` |
||||
|
- `/// warning | Попередження` |
||||
|
- `/// info | Інформація` |
||||
|
- `/// danger | Обережно` |
||||
@ -1,20 +0,0 @@ |
|||||
from typing import Annotated |
|
||||
|
|
||||
from fastapi import Cookie, FastAPI |
|
||||
from pydantic import BaseModel |
|
||||
|
|
||||
app = FastAPI() |
|
||||
|
|
||||
|
|
||||
class Cookies(BaseModel): |
|
||||
class Config: |
|
||||
extra = "forbid" |
|
||||
|
|
||||
session_id: str |
|
||||
fatebook_tracker: str | None = None |
|
||||
googall_tracker: str | None = None |
|
||||
|
|
||||
|
|
||||
@app.get("/items/") |
|
||||
async def read_items(cookies: Annotated[Cookies, Cookie()]): |
|
||||
return cookies |
|
||||
@ -1,20 +0,0 @@ |
|||||
from typing import Annotated, Union |
|
||||
|
|
||||
from fastapi import Cookie, FastAPI |
|
||||
from pydantic import BaseModel |
|
||||
|
|
||||
app = FastAPI() |
|
||||
|
|
||||
|
|
||||
class Cookies(BaseModel): |
|
||||
class Config: |
|
||||
extra = "forbid" |
|
||||
|
|
||||
session_id: str |
|
||||
fatebook_tracker: Union[str, None] = None |
|
||||
googall_tracker: Union[str, None] = None |
|
||||
|
|
||||
|
|
||||
@app.get("/items/") |
|
||||
async def read_items(cookies: Annotated[Cookies, Cookie()]): |
|
||||
return cookies |
|
||||
@ -1,18 +0,0 @@ |
|||||
from fastapi import Cookie, FastAPI |
|
||||
from pydantic import BaseModel |
|
||||
|
|
||||
app = FastAPI() |
|
||||
|
|
||||
|
|
||||
class Cookies(BaseModel): |
|
||||
class Config: |
|
||||
extra = "forbid" |
|
||||
|
|
||||
session_id: str |
|
||||
fatebook_tracker: str | None = None |
|
||||
googall_tracker: str | None = None |
|
||||
|
|
||||
|
|
||||
@app.get("/items/") |
|
||||
async def read_items(cookies: Cookies = Cookie()): |
|
||||
return cookies |
|
||||
@ -1,20 +0,0 @@ |
|||||
from typing import Union |
|
||||
|
|
||||
from fastapi import Cookie, FastAPI |
|
||||
from pydantic import BaseModel |
|
||||
|
|
||||
app = FastAPI() |
|
||||
|
|
||||
|
|
||||
class Cookies(BaseModel): |
|
||||
class Config: |
|
||||
extra = "forbid" |
|
||||
|
|
||||
session_id: str |
|
||||
fatebook_tracker: Union[str, None] = None |
|
||||
googall_tracker: Union[str, None] = None |
|
||||
|
|
||||
|
|
||||
@app.get("/items/") |
|
||||
async def read_items(cookies: Cookies = Cookie()): |
|
||||
return cookies |
|
||||
@ -1,22 +0,0 @@ |
|||||
from typing import Annotated |
|
||||
|
|
||||
from fastapi import FastAPI, Header |
|
||||
from pydantic import BaseModel |
|
||||
|
|
||||
app = FastAPI() |
|
||||
|
|
||||
|
|
||||
class CommonHeaders(BaseModel): |
|
||||
class Config: |
|
||||
extra = "forbid" |
|
||||
|
|
||||
host: str |
|
||||
save_data: bool |
|
||||
if_modified_since: str | None = None |
|
||||
traceparent: str | None = None |
|
||||
x_tag: list[str] = [] |
|
||||
|
|
||||
|
|
||||
@app.get("/items/") |
|
||||
async def read_items(headers: Annotated[CommonHeaders, Header()]): |
|
||||
return headers |
|
||||
@ -1,22 +0,0 @@ |
|||||
from typing import Annotated, Union |
|
||||
|
|
||||
from fastapi import FastAPI, Header |
|
||||
from pydantic import BaseModel |
|
||||
|
|
||||
app = FastAPI() |
|
||||
|
|
||||
|
|
||||
class CommonHeaders(BaseModel): |
|
||||
class Config: |
|
||||
extra = "forbid" |
|
||||
|
|
||||
host: str |
|
||||
save_data: bool |
|
||||
if_modified_since: Union[str, None] = None |
|
||||
traceparent: Union[str, None] = None |
|
||||
x_tag: list[str] = [] |
|
||||
|
|
||||
|
|
||||
@app.get("/items/") |
|
||||
async def read_items(headers: Annotated[CommonHeaders, Header()]): |
|
||||
return headers |
|
||||
@ -1,20 +0,0 @@ |
|||||
from fastapi import FastAPI, Header |
|
||||
from pydantic import BaseModel |
|
||||
|
|
||||
app = FastAPI() |
|
||||
|
|
||||
|
|
||||
class CommonHeaders(BaseModel): |
|
||||
class Config: |
|
||||
extra = "forbid" |
|
||||
|
|
||||
host: str |
|
||||
save_data: bool |
|
||||
if_modified_since: str | None = None |
|
||||
traceparent: str | None = None |
|
||||
x_tag: list[str] = [] |
|
||||
|
|
||||
|
|
||||
@app.get("/items/") |
|
||||
async def read_items(headers: CommonHeaders = Header()): |
|
||||
return headers |
|
||||
@ -1,22 +0,0 @@ |
|||||
from typing import Union |
|
||||
|
|
||||
from fastapi import FastAPI, Header |
|
||||
from pydantic import BaseModel |
|
||||
|
|
||||
app = FastAPI() |
|
||||
|
|
||||
|
|
||||
class CommonHeaders(BaseModel): |
|
||||
class Config: |
|
||||
extra = "forbid" |
|
||||
|
|
||||
host: str |
|
||||
save_data: bool |
|
||||
if_modified_since: Union[str, None] = None |
|
||||
traceparent: Union[str, None] = None |
|
||||
x_tag: list[str] = [] |
|
||||
|
|
||||
|
|
||||
@app.get("/items/") |
|
||||
async def read_items(headers: CommonHeaders = Header()): |
|
||||
return headers |
|
||||
@ -1,2 +1,2 @@ |
|||||
def get_items(item_a: str, item_b: int, item_c: float, item_d: bool, item_e: bytes): |
def get_items(item_a: str, item_b: int, item_c: float, item_d: bool, item_e: bytes): |
||||
return item_a, item_b, item_c, item_d, item_d, item_e |
return item_a, item_b, item_c, item_d, item_e |
||||
|
|||||
@ -1,21 +0,0 @@ |
|||||
from typing import Annotated, Literal |
|
||||
|
|
||||
from fastapi import FastAPI, Query |
|
||||
from pydantic import BaseModel, Field |
|
||||
|
|
||||
app = FastAPI() |
|
||||
|
|
||||
|
|
||||
class FilterParams(BaseModel): |
|
||||
class Config: |
|
||||
extra = "forbid" |
|
||||
|
|
||||
limit: int = Field(100, gt=0, le=100) |
|
||||
offset: int = Field(0, ge=0) |
|
||||
order_by: Literal["created_at", "updated_at"] = "created_at" |
|
||||
tags: list[str] = [] |
|
||||
|
|
||||
|
|
||||
@app.get("/items/") |
|
||||
async def read_items(filter_query: Annotated[FilterParams, Query()]): |
|
||||
return filter_query |
|
||||
@ -1,21 +0,0 @@ |
|||||
from typing import Annotated, Literal |
|
||||
|
|
||||
from fastapi import FastAPI, Query |
|
||||
from pydantic import BaseModel, Field |
|
||||
|
|
||||
app = FastAPI() |
|
||||
|
|
||||
|
|
||||
class FilterParams(BaseModel): |
|
||||
class Config: |
|
||||
extra = "forbid" |
|
||||
|
|
||||
limit: int = Field(100, gt=0, le=100) |
|
||||
offset: int = Field(0, ge=0) |
|
||||
order_by: Literal["created_at", "updated_at"] = "created_at" |
|
||||
tags: list[str] = [] |
|
||||
|
|
||||
|
|
||||
@app.get("/items/") |
|
||||
async def read_items(filter_query: Annotated[FilterParams, Query()]): |
|
||||
return filter_query |
|
||||
@ -1,21 +0,0 @@ |
|||||
from typing import Literal |
|
||||
|
|
||||
from fastapi import FastAPI, Query |
|
||||
from pydantic import BaseModel, Field |
|
||||
|
|
||||
app = FastAPI() |
|
||||
|
|
||||
|
|
||||
class FilterParams(BaseModel): |
|
||||
class Config: |
|
||||
extra = "forbid" |
|
||||
|
|
||||
limit: int = Field(100, gt=0, le=100) |
|
||||
offset: int = Field(0, ge=0) |
|
||||
order_by: Literal["created_at", "updated_at"] = "created_at" |
|
||||
tags: list[str] = [] |
|
||||
|
|
||||
|
|
||||
@app.get("/items/") |
|
||||
async def read_items(filter_query: FilterParams = Query()): |
|
||||
return filter_query |
|
||||
@ -1,21 +0,0 @@ |
|||||
from typing import Literal |
|
||||
|
|
||||
from fastapi import FastAPI, Query |
|
||||
from pydantic import BaseModel, Field |
|
||||
|
|
||||
app = FastAPI() |
|
||||
|
|
||||
|
|
||||
class FilterParams(BaseModel): |
|
||||
class Config: |
|
||||
extra = "forbid" |
|
||||
|
|
||||
limit: int = Field(100, gt=0, le=100) |
|
||||
offset: int = Field(0, ge=0) |
|
||||
order_by: Literal["created_at", "updated_at"] = "created_at" |
|
||||
tags: list[str] = [] |
|
||||
|
|
||||
|
|
||||
@app.get("/items/") |
|
||||
async def read_items(filter_query: FilterParams = Query()): |
|
||||
return filter_query |
|
||||
@ -0,0 +1,98 @@ |
|||||
|
import sys |
||||
|
|
||||
|
import pytest |
||||
|
|
||||
|
from tests.utils import skip_module_if_py_gte_314 |
||||
|
|
||||
|
if sys.version_info >= (3, 14): |
||||
|
skip_module_if_py_gte_314() |
||||
|
|
||||
|
from fastapi import FastAPI |
||||
|
from fastapi._compat.v1 import BaseModel |
||||
|
from fastapi.testclient import TestClient |
||||
|
|
||||
|
|
||||
|
def test_warns_pydantic_v1_model_in_endpoint_param() -> None: |
||||
|
class ParamModelV1(BaseModel): |
||||
|
name: str |
||||
|
|
||||
|
app = FastAPI() |
||||
|
|
||||
|
with pytest.warns( |
||||
|
DeprecationWarning, |
||||
|
match=r"pydantic\.v1 is deprecated.*Please update the param data:", |
||||
|
): |
||||
|
|
||||
|
@app.post("/param") |
||||
|
def endpoint(data: ParamModelV1): |
||||
|
return data |
||||
|
|
||||
|
client = TestClient(app) |
||||
|
response = client.post("/param", json={"name": "test"}) |
||||
|
assert response.status_code == 200, response.text |
||||
|
assert response.json() == {"name": "test"} |
||||
|
|
||||
|
|
||||
|
def test_warns_pydantic_v1_model_in_return_type() -> None: |
||||
|
class ReturnModelV1(BaseModel): |
||||
|
name: str |
||||
|
|
||||
|
app = FastAPI() |
||||
|
|
||||
|
with pytest.warns( |
||||
|
DeprecationWarning, |
||||
|
match=r"pydantic\.v1 is deprecated.*Please update the response model", |
||||
|
): |
||||
|
|
||||
|
@app.get("/return") |
||||
|
def endpoint() -> ReturnModelV1: |
||||
|
return ReturnModelV1(name="test") |
||||
|
|
||||
|
client = TestClient(app) |
||||
|
response = client.get("/return") |
||||
|
assert response.status_code == 200, response.text |
||||
|
assert response.json() == {"name": "test"} |
||||
|
|
||||
|
|
||||
|
def test_warns_pydantic_v1_model_in_response_model() -> None: |
||||
|
class ResponseModelV1(BaseModel): |
||||
|
name: str |
||||
|
|
||||
|
app = FastAPI() |
||||
|
|
||||
|
with pytest.warns( |
||||
|
DeprecationWarning, |
||||
|
match=r"pydantic\.v1 is deprecated.*Please update the response model", |
||||
|
): |
||||
|
|
||||
|
@app.get("/response-model", response_model=ResponseModelV1) |
||||
|
def endpoint(): |
||||
|
return {"name": "test"} |
||||
|
|
||||
|
client = TestClient(app) |
||||
|
response = client.get("/response-model") |
||||
|
assert response.status_code == 200, response.text |
||||
|
assert response.json() == {"name": "test"} |
||||
|
|
||||
|
|
||||
|
def test_warns_pydantic_v1_model_in_additional_responses_model() -> None: |
||||
|
class ErrorModelV1(BaseModel): |
||||
|
detail: str |
||||
|
|
||||
|
app = FastAPI() |
||||
|
|
||||
|
with pytest.warns( |
||||
|
DeprecationWarning, |
||||
|
match=r"pydantic\.v1 is deprecated.*In responses=\{\}, please update", |
||||
|
): |
||||
|
|
||||
|
@app.get( |
||||
|
"/responses", response_model=None, responses={400: {"model": ErrorModelV1}} |
||||
|
) |
||||
|
def endpoint(): |
||||
|
return {"ok": True} |
||||
|
|
||||
|
client = TestClient(app) |
||||
|
response = client.get("/responses") |
||||
|
assert response.status_code == 200, response.text |
||||
|
assert response.json() == {"ok": True} |
||||
@ -1,140 +1,137 @@ |
|||||
|
import warnings |
||||
|
|
||||
from fastapi import FastAPI |
from fastapi import FastAPI |
||||
|
|
||||
from . import modelsv1, modelsv2, modelsv2b |
from . import modelsv1, modelsv2, modelsv2b |
||||
|
|
||||
app = FastAPI() |
app = FastAPI() |
||||
|
|
||||
|
with warnings.catch_warnings(record=True): |
||||
|
warnings.simplefilter("always") |
||||
|
|
||||
@app.post("/v1-to-v2/item") |
@app.post("/v1-to-v2/item") |
||||
def handle_v1_item_to_v2(data: modelsv1.Item) -> modelsv2.Item: |
def handle_v1_item_to_v2(data: modelsv1.Item) -> modelsv2.Item: |
||||
return modelsv2.Item( |
return modelsv2.Item( |
||||
new_title=data.title, |
new_title=data.title, |
||||
new_size=data.size, |
new_size=data.size, |
||||
new_description=data.description, |
new_description=data.description, |
||||
new_sub=modelsv2.SubItem(new_sub_name=data.sub.name), |
new_sub=modelsv2.SubItem(new_sub_name=data.sub.name), |
||||
new_multi=[modelsv2.SubItem(new_sub_name=s.name) for s in data.multi], |
new_multi=[modelsv2.SubItem(new_sub_name=s.name) for s in data.multi], |
||||
) |
) |
||||
|
|
||||
|
|
||||
@app.post("/v2-to-v1/item") |
|
||||
def handle_v2_item_to_v1(data: modelsv2.Item) -> modelsv1.Item: |
|
||||
return modelsv1.Item( |
|
||||
title=data.new_title, |
|
||||
size=data.new_size, |
|
||||
description=data.new_description, |
|
||||
sub=modelsv1.SubItem(name=data.new_sub.new_sub_name), |
|
||||
multi=[modelsv1.SubItem(name=s.new_sub_name) for s in data.new_multi], |
|
||||
) |
|
||||
|
|
||||
|
|
||||
@app.post("/v1-to-v2/item-to-list") |
@app.post("/v2-to-v1/item") |
||||
def handle_v1_item_to_v2_list(data: modelsv1.Item) -> list[modelsv2.Item]: |
def handle_v2_item_to_v1(data: modelsv2.Item) -> modelsv1.Item: |
||||
converted = modelsv2.Item( |
return modelsv1.Item( |
||||
new_title=data.title, |
title=data.new_title, |
||||
new_size=data.size, |
size=data.new_size, |
||||
new_description=data.description, |
description=data.new_description, |
||||
new_sub=modelsv2.SubItem(new_sub_name=data.sub.name), |
sub=modelsv1.SubItem(name=data.new_sub.new_sub_name), |
||||
new_multi=[modelsv2.SubItem(new_sub_name=s.name) for s in data.multi], |
multi=[modelsv1.SubItem(name=s.new_sub_name) for s in data.new_multi], |
||||
) |
) |
||||
return [converted, converted] |
|
||||
|
|
||||
|
@app.post("/v1-to-v2/item-to-list") |
||||
|
def handle_v1_item_to_v2_list(data: modelsv1.Item) -> list[modelsv2.Item]: |
||||
|
converted = modelsv2.Item( |
||||
|
new_title=data.title, |
||||
|
new_size=data.size, |
||||
|
new_description=data.description, |
||||
|
new_sub=modelsv2.SubItem(new_sub_name=data.sub.name), |
||||
|
new_multi=[modelsv2.SubItem(new_sub_name=s.name) for s in data.multi], |
||||
|
) |
||||
|
return [converted, converted] |
||||
|
|
||||
|
@app.post("/v1-to-v2/list-to-list") |
||||
|
def handle_v1_list_to_v2_list(data: list[modelsv1.Item]) -> list[modelsv2.Item]: |
||||
|
result = [] |
||||
|
for item in data: |
||||
|
result.append( |
||||
|
modelsv2.Item( |
||||
|
new_title=item.title, |
||||
|
new_size=item.size, |
||||
|
new_description=item.description, |
||||
|
new_sub=modelsv2.SubItem(new_sub_name=item.sub.name), |
||||
|
new_multi=[ |
||||
|
modelsv2.SubItem(new_sub_name=s.name) for s in item.multi |
||||
|
], |
||||
|
) |
||||
|
) |
||||
|
return result |
||||
|
|
||||
@app.post("/v1-to-v2/list-to-list") |
@app.post("/v1-to-v2/list-to-item") |
||||
def handle_v1_list_to_v2_list(data: list[modelsv1.Item]) -> list[modelsv2.Item]: |
def handle_v1_list_to_v2_item(data: list[modelsv1.Item]) -> modelsv2.Item: |
||||
result = [] |
if data: |
||||
for item in data: |
item = data[0] |
||||
result.append( |
return modelsv2.Item( |
||||
modelsv2.Item( |
|
||||
new_title=item.title, |
new_title=item.title, |
||||
new_size=item.size, |
new_size=item.size, |
||||
new_description=item.description, |
new_description=item.description, |
||||
new_sub=modelsv2.SubItem(new_sub_name=item.sub.name), |
new_sub=modelsv2.SubItem(new_sub_name=item.sub.name), |
||||
new_multi=[modelsv2.SubItem(new_sub_name=s.name) for s in item.multi], |
new_multi=[modelsv2.SubItem(new_sub_name=s.name) for s in item.multi], |
||||
) |
) |
||||
) |
|
||||
return result |
|
||||
|
|
||||
|
|
||||
@app.post("/v1-to-v2/list-to-item") |
|
||||
def handle_v1_list_to_v2_item(data: list[modelsv1.Item]) -> modelsv2.Item: |
|
||||
if data: |
|
||||
item = data[0] |
|
||||
return modelsv2.Item( |
return modelsv2.Item( |
||||
new_title=item.title, |
new_title="", new_size=0, new_sub=modelsv2.SubItem(new_sub_name="") |
||||
new_size=item.size, |
|
||||
new_description=item.description, |
|
||||
new_sub=modelsv2.SubItem(new_sub_name=item.sub.name), |
|
||||
new_multi=[modelsv2.SubItem(new_sub_name=s.name) for s in item.multi], |
|
||||
) |
) |
||||
return modelsv2.Item( |
|
||||
new_title="", new_size=0, new_sub=modelsv2.SubItem(new_sub_name="") |
|
||||
) |
|
||||
|
|
||||
|
|
||||
@app.post("/v2-to-v1/item-to-list") |
|
||||
def handle_v2_item_to_v1_list(data: modelsv2.Item) -> list[modelsv1.Item]: |
|
||||
converted = modelsv1.Item( |
|
||||
title=data.new_title, |
|
||||
size=data.new_size, |
|
||||
description=data.new_description, |
|
||||
sub=modelsv1.SubItem(name=data.new_sub.new_sub_name), |
|
||||
multi=[modelsv1.SubItem(name=s.new_sub_name) for s in data.new_multi], |
|
||||
) |
|
||||
return [converted, converted] |
|
||||
|
|
||||
|
@app.post("/v2-to-v1/item-to-list") |
||||
|
def handle_v2_item_to_v1_list(data: modelsv2.Item) -> list[modelsv1.Item]: |
||||
|
converted = modelsv1.Item( |
||||
|
title=data.new_title, |
||||
|
size=data.new_size, |
||||
|
description=data.new_description, |
||||
|
sub=modelsv1.SubItem(name=data.new_sub.new_sub_name), |
||||
|
multi=[modelsv1.SubItem(name=s.new_sub_name) for s in data.new_multi], |
||||
|
) |
||||
|
return [converted, converted] |
||||
|
|
||||
|
@app.post("/v2-to-v1/list-to-list") |
||||
|
def handle_v2_list_to_v1_list(data: list[modelsv2.Item]) -> list[modelsv1.Item]: |
||||
|
result = [] |
||||
|
for item in data: |
||||
|
result.append( |
||||
|
modelsv1.Item( |
||||
|
title=item.new_title, |
||||
|
size=item.new_size, |
||||
|
description=item.new_description, |
||||
|
sub=modelsv1.SubItem(name=item.new_sub.new_sub_name), |
||||
|
multi=[ |
||||
|
modelsv1.SubItem(name=s.new_sub_name) for s in item.new_multi |
||||
|
], |
||||
|
) |
||||
|
) |
||||
|
return result |
||||
|
|
||||
@app.post("/v2-to-v1/list-to-list") |
@app.post("/v2-to-v1/list-to-item") |
||||
def handle_v2_list_to_v1_list(data: list[modelsv2.Item]) -> list[modelsv1.Item]: |
def handle_v2_list_to_v1_item(data: list[modelsv2.Item]) -> modelsv1.Item: |
||||
result = [] |
if data: |
||||
for item in data: |
item = data[0] |
||||
result.append( |
return modelsv1.Item( |
||||
modelsv1.Item( |
|
||||
title=item.new_title, |
title=item.new_title, |
||||
size=item.new_size, |
size=item.new_size, |
||||
description=item.new_description, |
description=item.new_description, |
||||
sub=modelsv1.SubItem(name=item.new_sub.new_sub_name), |
sub=modelsv1.SubItem(name=item.new_sub.new_sub_name), |
||||
multi=[modelsv1.SubItem(name=s.new_sub_name) for s in item.new_multi], |
multi=[modelsv1.SubItem(name=s.new_sub_name) for s in item.new_multi], |
||||
) |
) |
||||
) |
return modelsv1.Item(title="", size=0, sub=modelsv1.SubItem(name="")) |
||||
return result |
|
||||
|
|
||||
|
|
||||
@app.post("/v2-to-v1/list-to-item") |
@app.post("/v2-to-v1/same-name") |
||||
def handle_v2_list_to_v1_item(data: list[modelsv2.Item]) -> modelsv1.Item: |
def handle_v2_same_name_to_v1( |
||||
if data: |
item1: modelsv2.Item, item2: modelsv2b.Item |
||||
item = data[0] |
) -> modelsv1.Item: |
||||
return modelsv1.Item( |
return modelsv1.Item( |
||||
title=item.new_title, |
title=item1.new_title, |
||||
size=item.new_size, |
size=item2.dup_size, |
||||
description=item.new_description, |
description=item1.new_description, |
||||
sub=modelsv1.SubItem(name=item.new_sub.new_sub_name), |
sub=modelsv1.SubItem(name=item1.new_sub.new_sub_name), |
||||
multi=[modelsv1.SubItem(name=s.new_sub_name) for s in item.new_multi], |
multi=[modelsv1.SubItem(name=s.dup_sub_name) for s in item2.dup_multi], |
||||
) |
) |
||||
return modelsv1.Item(title="", size=0, sub=modelsv1.SubItem(name="")) |
|
||||
|
|
||||
|
|
||||
@app.post("/v2-to-v1/same-name") |
|
||||
def handle_v2_same_name_to_v1( |
|
||||
item1: modelsv2.Item, item2: modelsv2b.Item |
|
||||
) -> modelsv1.Item: |
|
||||
return modelsv1.Item( |
|
||||
title=item1.new_title, |
|
||||
size=item2.dup_size, |
|
||||
description=item1.new_description, |
|
||||
sub=modelsv1.SubItem(name=item1.new_sub.new_sub_name), |
|
||||
multi=[modelsv1.SubItem(name=s.dup_sub_name) for s in item2.dup_multi], |
|
||||
) |
|
||||
|
|
||||
|
|
||||
@app.post("/v2-to-v1/list-of-items-to-list-of-items") |
@app.post("/v2-to-v1/list-of-items-to-list-of-items") |
||||
def handle_v2_items_in_list_to_v1_item_in_list( |
def handle_v2_items_in_list_to_v1_item_in_list( |
||||
data1: list[modelsv2.ItemInList], data2: list[modelsv2b.ItemInList] |
data1: list[modelsv2.ItemInList], data2: list[modelsv2b.ItemInList] |
||||
) -> list[modelsv1.ItemInList]: |
) -> list[modelsv1.ItemInList]: |
||||
result = [] |
item1 = data1[0] |
||||
item1 = data1[0] |
item2 = data2[0] |
||||
item2 = data2[0] |
return [ |
||||
result = [ |
modelsv1.ItemInList(name1=item1.name2), |
||||
modelsv1.ItemInList(name1=item1.name2), |
modelsv1.ItemInList(name1=item2.dup_name2), |
||||
modelsv1.ItemInList(name1=item2.dup_name2), |
] |
||||
] |
|
||||
return result |
|
||||
|
|||||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue