Skip to content

Commit ab2b86f

Browse files
dmontagutiangolo
authored andcommitted
✨ Add support for Pydantic v1 and above 🎉 (fastapi#646)
* Make compatible with pydantic v1 * Remove unused import * Remove unused ignores * Update pydantic version * Fix minor formatting issue * ⏪ Revert removing iterate_in_threadpool * ✨ Add backwards compatibility with Pydantic 0.32.2 with deprecation warnings * ✅ Update tests to not break when using Pydantic < 1.0.0 * 📝 Update docs for Pydantic version 1.0.0 * 📌 Update Pydantic range version to support from 0.32.2 * 🎨 Format test imports * ✨ Add support for Pydantic < 1.2 for populate_validators * ✨ Add backwards compatibility for Pydantic < 1.2.0 with required fields * 📌 Relax requirement for Pydantic to < 2.0.0 🎉 🚀 * 💚 Update pragma coverage for older Pydantic versions
1 parent 90a5796 commit ab2b86f

File tree

66 files changed

+802
-425
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

66 files changed

+802
-425
lines changed

Pipfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ uvicorn = "*"
2626

2727
[packages]
2828
starlette = "==0.12.9"
29-
pydantic = "==0.32.2"
29+
pydantic = "==1.0.0"
3030
databases = {extras = ["sqlite"],version = "*"}
3131
hypercorn = "*"
3232
orjson = "*"

docs/src/body_schema/tutorial001.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
from fastapi import Body, FastAPI
2-
from pydantic import BaseModel, Schema
2+
from pydantic import BaseModel, Field
33

44
app = FastAPI()
55

66

77
class Item(BaseModel):
88
name: str
9-
description: str = Schema(None, title="The description of the item", max_length=300)
10-
price: float = Schema(..., gt=0, description="The price must be greater than zero")
9+
description: str = Field(None, title="The description of the item", max_length=300)
10+
price: float = Field(..., gt=0, description="The price must be greater than zero")
1111
tax: float = None
1212

1313

docs/src/body_updates/tutorial002.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ async def read_item(item_id: str):
3131
async def update_item(item_id: str, item: Item):
3232
stored_item_data = items[item_id]
3333
stored_item_model = Item(**stored_item_data)
34-
update_data = item.dict(skip_defaults=True)
34+
update_data = item.dict(exclude_unset=True)
3535
updated_item = stored_item_model.copy(update=update_data)
3636
items[item_id] = jsonable_encoder(updated_item)
3737
return updated_item

docs/src/extra_models/tutorial001.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
from fastapi import FastAPI
2-
from pydantic import BaseModel
3-
from pydantic.types import EmailStr
2+
from pydantic import BaseModel, EmailStr
43

54
app = FastAPI()
65

docs/src/extra_models/tutorial002.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
from fastapi import FastAPI
2-
from pydantic import BaseModel
3-
from pydantic.types import EmailStr
2+
from pydantic import BaseModel, EmailStr
43

54
app = FastAPI()
65

docs/src/response_model/tutorial002.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
from fastapi import FastAPI
2-
from pydantic import BaseModel
3-
from pydantic.types import EmailStr
2+
from pydantic import BaseModel, EmailStr
43

54
app = FastAPI()
65

docs/src/response_model/tutorial003.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
from fastapi import FastAPI
2-
from pydantic import BaseModel
3-
from pydantic.types import EmailStr
2+
from pydantic import BaseModel, EmailStr
43

54
app = FastAPI()
65

docs/src/response_model/tutorial004.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,6 @@ class Item(BaseModel):
2121
}
2222

2323

24-
@app.get("/items/{item_id}", response_model=Item, response_model_skip_defaults=True)
24+
@app.get("/items/{item_id}", response_model=Item, response_model_exclude_unset=True)
2525
async def read_item(item_id: str):
2626
return items[item_id]

docs/tutorial/body-schema.md

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
The same way you can declare additional validation and metadata in path operation function parameters with `Query`, `Path` and `Body`, you can declare validation and metadata inside of Pydantic models using `Schema`.
1+
The same way you can declare additional validation and metadata in path operation function parameters with `Query`, `Path` and `Body`, you can declare validation and metadata inside of Pydantic models using Pydantic's `Field`.
22

3-
## Import Schema
3+
## Import `Field`
44

55
First, you have to import it:
66

@@ -9,32 +9,34 @@ First, you have to import it:
99
```
1010

1111
!!! warning
12-
Notice that `Schema` is imported directly from `pydantic`, not from `fastapi` as are all the rest (`Query`, `Path`, `Body`, etc).
12+
Notice that `Field` is imported directly from `pydantic`, not from `fastapi` as are all the rest (`Query`, `Path`, `Body`, etc).
1313

1414

1515
## Declare model attributes
1616

17-
You can then use `Schema` with model attributes:
17+
You can then use `Field` with model attributes:
1818

1919
```Python hl_lines="9 10"
2020
{!./src/body_schema/tutorial001.py!}
2121
```
2222

23-
`Schema` works the same way as `Query`, `Path` and `Body`, it has all the same parameters, etc.
23+
`Field` works the same way as `Query`, `Path` and `Body`, it has all the same parameters, etc.
2424

2525
!!! note "Technical Details"
26-
Actually, `Query`, `Path` and others you'll see next are subclasses of a common `Param` which is itself a subclass of Pydantic's `Schema`.
26+
Actually, `Query`, `Path` and others you'll see next create objects of subclasses of a common `Param` class, which is itself a subclass of Pydantic's `FieldInfo` class.
2727

28-
`Body` is also a subclass of `Schema` directly. And there are others you will see later that are subclasses of `Body`.
28+
And Pydantic's `Field` returns an instance of `FieldInfo` as well.
2929

30-
But remember that when you import `Query`, `Path` and others from `fastapi`, <a href="https://fastapi.tiangolo.com/tutorial/path-params-numeric-validations/#recap" target="_blank">those are actually functions that return classes of the same name</a>.
30+
`Body` also returns objects of a subclass of `FieldInfo` directly. And there are others you will see later that are subclasses of the `Body` class.
31+
32+
Remember that when you import `Query`, `Path`, and others from `fastapi`, <a href="https://fastapi.tiangolo.com/tutorial/path-params-numeric-validations/#recap" target="_blank">those are actually functions that return classes of the same name</a>.
3133

3234
!!! tip
33-
Notice how each model's attribute with a type, default value and `Schema` has the same structure as a path operation function's parameter, with `Schema` instead of `Path`, `Query` and `Body`.
35+
Notice how each model's attribute with a type, default value and `Field` has the same structure as a path operation function's parameter, with `Field` instead of `Path`, `Query` and `Body`.
3436

3537
## Schema extras
3638

37-
In `Schema`, `Path`, `Query`, `Body` and others you'll see later, you can declare extra parameters apart from those described before.
39+
In `Field`, `Path`, `Query`, `Body` and others you'll see later, you can declare extra parameters apart from those described before.
3840

3941
Those parameters will be added as-is to the output JSON Schema.
4042

@@ -55,6 +57,6 @@ And it would look in the `/docs` like this:
5557

5658
## Recap
5759

58-
You can use Pydantic's `Schema` to declare extra validations and metadata for model attributes.
60+
You can use Pydantic's `Field` to declare extra validations and metadata for model attributes.
5961

60-
You can also use the extra keyword arguments to pass additional JSON Schema metadata.
62+
You can also use the extra keyword arguments to pass additional JSON Schema metadata.

docs/tutorial/body-updates.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,15 +41,15 @@ This means that you can send only the data that you want to update, leaving the
4141

4242
But this guide shows you, more or less, how they are intended to be used.
4343

44-
### Using Pydantic's `skip_defaults` parameter
44+
### Using Pydantic's `exclude_unset` parameter
4545

46-
If you want to receive partial updates, it's very useful to use the parameter `skip_defaults` in Pydantic's model's `.dict()`.
46+
If you want to receive partial updates, it's very useful to use the parameter `exclude_unset` in Pydantic's model's `.dict()`.
4747

48-
Like `item.dict(skip_defaults=True)`.
48+
Like `item.dict(exclude_unset=True)`.
4949

5050
That would generate a `dict` with only the data that was set when creating the `item` model, excluding default values.
5151

52-
Then you can use this to generate a `dict` with only the data that was set, omitting default values:
52+
Then you can use this to generate a `dict` with only the data that was set (sent in the request), omitting default values:
5353

5454
```Python hl_lines="34"
5555
{!./src/body_updates/tutorial002.py!}
@@ -72,7 +72,7 @@ In summary, to apply partial updates you would:
7272
* (Optionally) use `PATCH` instead of `PUT`.
7373
* Retrieve the stored data.
7474
* Put that data in a Pydantic model.
75-
* Generate a `dict` without default values from the input model (using `skip_defaults`).
75+
* Generate a `dict` without default values from the input model (using `exclude_unset`).
7676
* This way you can update only the values actually set by the user, instead of overriding values already stored with default values in your model.
7777
* Create a copy of the stored model, updating it's attributes with the received partial updates (using the `update` parameter).
7878
* Convert the copied model to something that can be stored in your DB (for example, using the `jsonable_encoder`).

docs/tutorial/extra-models.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ This is especially the case for user models, because:
1515

1616
Here's a general idea of how the models could look like with their password fields and the places where they are used:
1717

18-
```Python hl_lines="8 10 15 21 23 32 34 39 40"
18+
```Python hl_lines="7 9 14 20 22 27 28 31 32 33 38 39"
1919
{!./src/extra_models/tutorial001.py!}
2020
```
2121

@@ -148,7 +148,7 @@ All the data conversion, validation, documentation, etc. will still work as norm
148148

149149
That way, we can declare just the differences between the models (with plaintext `password`, with `hashed_password` and without password):
150150

151-
```Python hl_lines="8 14 15 18 19 22 23"
151+
```Python hl_lines="7 13 14 17 18 21 22"
152152
{!./src/extra_models/tutorial002.py!}
153153
```
154154

docs/tutorial/response-model.md

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,13 @@ But most importantly:
3333

3434
Here we are declaring a `UserIn` model, it will contain a plaintext password:
3535

36-
```Python hl_lines="8 10"
36+
```Python hl_lines="7 9"
3737
{!./src/response_model/tutorial002.py!}
3838
```
3939

4040
And we are using this model to declare our input and the same model to declare our output:
4141

42-
```Python hl_lines="16 17"
42+
```Python hl_lines="15 16"
4343
{!./src/response_model/tutorial002.py!}
4444
```
4545

@@ -56,19 +56,19 @@ But if we use the same model for another path operation, we could be sending our
5656

5757
We can instead create an input model with the plaintext password and an output model without it:
5858

59-
```Python hl_lines="8 10 15"
59+
```Python hl_lines="7 9 14"
6060
{!./src/response_model/tutorial003.py!}
6161
```
6262

6363
Here, even though our path operation function is returning the same input user that contains the password:
6464

65-
```Python hl_lines="23"
65+
```Python hl_lines="22"
6666
{!./src/response_model/tutorial003.py!}
6767
```
6868

6969
...we declared the `response_model` to be our model `UserOut`, that doesn't include the password:
7070

71-
```Python hl_lines="21"
71+
```Python hl_lines="20"
7272
{!./src/response_model/tutorial003.py!}
7373
```
7474

@@ -100,15 +100,15 @@ but you might want to omit them from the result if they were not actually stored
100100

101101
For example, if you have models with many optional attributes in a NoSQL database, but you don't want to send very long JSON responses full of default values.
102102

103-
### Use the `response_model_skip_defaults` parameter
103+
### Use the `response_model_exclude_unset` parameter
104104

105-
You can set the *path operation decorator* parameter `response_model_skip_defaults=True`:
105+
You can set the *path operation decorator* parameter `response_model_exclude_unset=True`:
106106

107107
```Python hl_lines="24"
108108
{!./src/response_model/tutorial004.py!}
109109
```
110110

111-
and those default values won't be included in the response.
111+
and those default values won't be included in the response, only the values actually set.
112112

113113
So, if you send a request to that *path operation* for the item with ID `foo`, the response (not including default values) will be:
114114

@@ -120,7 +120,7 @@ So, if you send a request to that *path operation* for the item with ID `foo`, t
120120
```
121121

122122
!!! info
123-
FastAPI uses Pydantic model's `.dict()` with <a href="https://pydantic-docs.helpmanual.io/#copying" target="_blank">its `skip_defaults` parameter</a> to achieve this.
123+
FastAPI uses Pydantic model's `.dict()` with <a href="https://pydantic-docs.helpmanual.io/usage/exporting_models/#modeldict" target="_blank">its `exclude_unset` parameter</a> to achieve this.
124124

125125
#### Data with values for fields with defaults
126126

@@ -194,4 +194,4 @@ If you forget to use a `set` and use a `list` or `tuple` instead, FastAPI will s
194194

195195
Use the path operation decorator's parameter `response_model` to define response models and especially to ensure private data is filtered out.
196196

197-
Use `response_model_skip_defaults` to return only the values explicitly set.
197+
Use `response_model_exclude_unset` to return only the values explicitly set.

0 commit comments

Comments
 (0)