8000 Reorganize `extra` configuration documentation (#11213) · pydantic/pydantic@a2eb22b · GitHub
[go: up one dir, main page]

Skip to content

Commit a2eb22b

Browse files
authored
Reorganize extra configuration documentation (#11213)
The section in the concepts page was moved to the top, as the default behavior is usually confusing for some users. The section was also simplified to just give some insights, and we refer to the API docs to avoid duplication. The API documentation was updated to take examples from both the existing concepts section and API documentation. Add more info about support for extra data with dataclasses Remove unused annotation comment.
1 parent da0449e commit a2eb22b

File tree

4 files changed

+130
-145
lines changed

4 files changed

+130
-145
lines changed

docs/concepts/config.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ except ValidationError as e:
4545
"""
4646
```
4747

48-
1. See the [Extra Attributes](models.md#extra-fields) section for more details.
48+
1. See the [Extra data](models.md#extra-data) section for more details.
4949

5050
Similarly, if using the [`@dataclass`][pydantic.dataclasses] decorator from Pydantic:
5151
```python
@@ -142,7 +142,7 @@ class Parent(BaseModel):
142142

143143

144144
class Model(Parent):
145-
model_config = ConfigDict(str_to_lower=True) # (1)!
145+
model_config = ConfigDict(str_to_lower=True)
146146

147147
x: str
148148

docs/concepts/dataclasses.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ class MyDataclass2:
141141
While Pydantic dataclasses support the [`extra`][pydantic.config.ConfigDict.extra] configuration value, some default
142142
behavior of stdlib dataclasses may prevail. For example, any extra fields present on a Pydantic dataclass with
143143
[`extra`][pydantic.config.ConfigDict.extra] set to `'allow'` are omitted in the dataclass' string representation.
144+
There is also no way to provide validation [using the `__pydantic_extra__` attribute](./models.md#extra-data).
144145

145146
## Rebuilding dataclass schema
146147

docs/concepts/models.md

Lines changed: 50 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,55 @@ print(Model(items=(1, 2, 3)))
221221
Besides, using these abstract types can also lead to [poor validation performance](./performance.md#sequence-vs-list-or-tuple-with-mapping-vs-dict), and in general using concrete container types
222222
will avoid unnecessary checks.
223223

224+
<!-- old anchor added for backwards compatibility -->
225+
[](){#extra-fields}
226+
227+
## Extra data
228+
229+
By default, Pydantic models **won't error when you provide extra data**, and these values will simply be ignored:
230+
231+
```python
232+
from pydantic import BaseModel
233+
234+
235+
class Model(BaseModel):
236+
x: int
237+
238+
239+
m = Model(x=1, y='a')
240+
assert m.model_dump() == {'x': 1}
241+
```
242+
243+
The [`extra`][pydantic.ConfigDict.extra] configuration value can be used to control this behavior:
244+
245+
```python
246+
from pydantic import BaseModel, ConfigDict
247+
248+
249+
class Model(BaseModel):
250+
x: int
251+
252+
model_config = ConfigDict(extra='allow')
253+
254+
255+
m = Model(x=1, y='a') # (1)!
256+
assert m.model_dump() == {'x': 1, 'y': 'a'}
257+
assert m.__pydantic_extra__ == {'y': 'a'}
258+
```
259+
260+
1. If [`extra`][pydantic.ConfigDict.extra] was set to `'forbid'`, this would fail.
261+
262+
The configuration can take three values:
263+
264+
- `'ignore'`: Providing extra data is ignored (the default).
265+
- `'forbid'`: Providing extra data is not permitted.
266+
- `'allow'`: Providing extra data is allowed and stored in the `__pydantic_extra__` dictionary attribute.
267+
The `__pydantic_extra__` can explicitly be annotated to provide validation for extra fields.
268+
269+
For more details, refer to the [`extra`][pydantic.ConfigDict.extra] API documentation.
270+
271+
Pydantic dataclasses also support extra data (see the [dataclass configuration](./dataclasses.md#dataclass-config) section).
272+
224273
## Nested models
225274

226275
More complex hierarchical data structures can be defined using models themselves as types in annotations.
@@ -639,7 +688,7 @@ Here are some additional notes on the behavior of [`model_construct()`][pydantic
639688
creating the model with validation.
640689
* No `__init__` method from the model or any of its parent classes will be called, even when a custom `__init__` method is defined.
641690

642-
!!! note "On [extra fields](#extra-fields) behavior with [`model_construct()`][pydantic.main.BaseModel.model_construct]"
691+
!!! note "On [extra data](#extra-data) behavior with [`model_construct()`][pydantic.main.BaseModel.model_construct]"
643692
* For models with [`extra`][pydantic.ConfigDict.extra] set to `'allow'`, data not corresponding to fields will be correctly stored in
644693
the `__pydantic_extra__` dictionary and saved to the model's `__dict__` attribute.
645694
* For models with [`extra`][pydantic.ConfigDict.extra] set to `'ignore'`, data not corresponding to fields will be ignored — that is,
@@ -1692,100 +1741,3 @@ print(f'{id(c1.arr) == id(c2.arr)=}')
16921741
There are some situations where Pydantic does not copy attributes, such as when passing models &mdash; we use the
16931742
model as is. You can override this behaviour by setting
16941743
[`model_config['revalidate_instances'] = 'always'`](../api/config.md#pydantic.config.ConfigDict).
1695-
1696-
## Extra fields
1697-
1698-
By default, Pydantic models won't error when you provide data for unrecognized fields, they will just be ignored:
1699-
1700-
```python
1701-
from pydantic import BaseModel
1702-
1703-
1704-
class Model(BaseModel):
1705-
x: int
1706-
1707-
1708-
m = Model(x=1, y='a')
1709-
assert m.model_dump() == {'x': 1}
1710-
```
1711-
1712-
If you want this to raise an error, you can set the [`extra`][pydantic.ConfigDict.extra] configuration
1713-
value to `'forbid'`:
1714-
1715-
```python
1716-
from pydantic import BaseModel, ConfigDict, ValidationError
1717-
1718-
1719-
class Model(BaseModel):
1720-
x: int
1721-
1722-
model_config = ConfigDict(extra='forbid')
1723-
1724-
1725-
try:
1726-
Model(x=1, y='a')
1727-
except ValidationError as exc:
1728-
print(exc)
1729-
"""
1730-
1 validation error for Model
1731-
y
1732-
Extra inputs are not permitted [type=extra_forbidden, input_value='a', input_type=str]
1733-
"""
1734-
```
1735-
1736-
To instead preserve any extra data provided, you can set [`extra`][pydantic.ConfigDict.extra] to `'allow'`.
1737-
The extra fields will then be stored in `BaseModel.__pydantic_extra__`:
1738-
1739-
```python
1740-
from pydantic import BaseModel, ConfigDict
1741-
1742-
1743-
class Model(BaseModel):
1744-
x: int
1745-
1746-
model_config = ConfigDict(extra='allow')
1747-
1748-
1749-
m = Model(x=1, y='a')
1750-
assert m.__pydantic_extra__ == {'y': 'a'}
1751-
```
1752-
1753-
By default, no validation will be applied to these extra items, but you can set a type for the values by overriding
1754-
the type annotation for `__pydantic_extra__`:
1755-
1756-
```python
1757-
from typing import Dict
1758-
1759-
from pydantic import BaseModel, ConfigDict, Field, ValidationError
1760-
1761-
1762-
class Model(BaseModel):
1763-
__pydantic_extra__: Dict[str, int] = Field(init=False) # (1)!
1764-
1765-
x: int
1766-
1767-
model_config = ConfigDict(extra='allow')
1768-
1769-
1770-
try:
1771-
Model(x=1, y='a')
1772-
except ValidationError as exc:
1773-
print(exc)
1774-
"""
1775-
1 validation error for Model
1776-
y
1777-
Input should be a valid integer, unable to parse string as an integer [type=int_parsing, input_value='a', input_type=str]
1778-
"""
1779-
1780-
m = Model(x=1, y='2')
1781-
assert m.x == 1
1782-
assert m.y == 2
1783-
assert m.model_dump() == {'x': 1, 'y': 2}
1784-
assert m.__pydantic_extra__ == {'y': 2}
1785-
```
1786-
1787-
1. The `= Field(init=False)` does not have any effect at runtime, but prevents the `__pydantic_extra__` field from
1788-
being included as a parameter to the model's `__init__` method by type checkers.
1789-
1790-
The same configurations apply to `TypedDict` and `dataclass`' except the config is controlled by setting the
1791-
`__pydantic_config__` attribute of the class to a valid `ConfigDict`.

pydantic/config.py

Lines changed: 77 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -59,69 +59,101 @@ class ConfigDict(TypedDict, total=False):
5959
"""The maximum length for str types. Defaults to `None`."""
6060

6161
extra: ExtraValues | None
62-
"""
63-
Whether to ignore, allow, or forbid extra attributes during model initialization. Defaults to `'ignore'`.
62+
'''
63+
Whether to ignore, allow, or forbid extra data during model initialization. Defaults to `'ignore'`.
6464
65-
You can configure how pydantic handles the attributes that are not defined in the model:
65+
Three configuration values are available:
6666
67-
* `allow` - Allow any extra attributes.
68-
* `forbid` - Forbid any extra attributes.
69-
* `ignore` - Ignore any extra attributes.
67+
- `'ignore'`: Providing extra data is ignored (the default):
68+
```python
69+
from pydantic import BaseModel, ConfigDict
7070
71-
```python
72-
from pydantic import BaseModel, ConfigDict
71+
class User(BaseModel):
72+
model_config = ConfigDict(extra='ignore') # (1)!
7373
74-
class User(BaseModel):
75-
model_config = ConfigDict(extra='ignore') # (1)!
74+
name: str
7675
77-
name: str
76+
user = User(name='John Doe', age=20) # (2)!
77+
print(user)
78+
#> name='John Doe'
79+
```
7880
79-
user = User(name='John Doe', age=20) # (2)!
80-
print(user)
81-
#> name='John Doe'
82-
```
81+
1. This is the default behaviour.
82+
2. The `age` argument is ignored.
8383
84-
1. This is the default behaviour.
85-
2. The `age` argument is ignored.
84+
- `'forbid'`: Providing extra data is not permitted, and a [`ValidationError`][pydantic_core.ValidationError]
85+
will be raised if this is the case:
86+
```python
87+
from pydantic import BaseModel, ConfigDict, ValidationError
8688
87-
Instead, with `extra='allow'`, the `age` argument is included:
8889
89-
```python
90-
from pydantic import BaseModel, ConfigDict
90+
class Model(BaseModel):
91+
x: int
9192
92-
class User(BaseModel):
93-
model_config = ConfigDict(extra='allow')
93+
model_config = ConfigDict(extra='forbid')
9494
95-
name: str
9695
97-
user = User(name='John Doe', age=20) # (1)!
98-
print(user)
99-
#> name='John Doe' age=20
100-
```
96+
try:
97+
Model(x=1, y='a')
98+
except ValidationError as exc:
99+
print(exc)
100+
"""
101+
1 validation error for Model
102+
y
103+
Extra inputs are not permitted [type=extra_forbidden, input_value='a', input_type=str]
104+
"""
105+
```
101106
102-
1. The `age` argument is included.
107+
- `'allow'`: Providing extra data is allowed and stored in the `__pydantic_extra__` dictionary attribute:
108+
```python
109+
from pydantic import BaseModel, ConfigDict
103110
104-
With `extra='forbid'`, an error is raised:
105111
106-
```python
107-
from pydantic import BaseModel, ConfigDict, ValidationError
112+
class Model(BaseModel):
113+
x: int
108114
109-
class User(BaseModel):
110-
model_config = ConfigDict(extra='forbid')
115+
model_config = ConfigDict(extra='allow')
111116
112-
name: str
113117
114-
try:
115-
User(name='John Doe', age=20)
116-
except ValidationError as e:
117-
print(e)
118-
'''
119-
1 validation error for User
120-
age
121-
Extra inputs are not permitted [type=extra_forbidden, input_value=20, input_type=int]
122-
'''
123-
```
124-
"""
118+
m = Model(x=1, y='a')
119+
assert m.__pydantic_extra__ == {'y': 'a'}
120+
```
121+
By default, no validation will be applied to these extra items, but you can set a type for the values by overriding
122+
the type annotation for `__pydantic_extra__`:
123+
```python
124+
from typing import Dict
125+
126+
from pydantic import BaseModel, ConfigDict, Field, ValidationError
127+
128+
129+
class Model(BaseModel):
130+
__pydantic_extra__: Dict[str, int] = Field(init=False) # (1)!
131+
132+
x: int
133+
134+
model_config = ConfigDict(extra='allow')
135+
136+
137+
try:
138+
Model(x=1, y='a')
139+
except ValidationError as exc:
140+
print(exc)
141+
"""
142+
1 validation error for Model
143+
y
144+
Input should be a valid integer, unable to parse string as an integer [type=int_parsing, input_value='a', input_type=str]
145+
"""
146+
147+
m = Model(x=1, y='2')
148+
assert m.x == 1
149+
assert m.y == 2
150+
assert m.model_dump() == {'x': 1, 'y': 2}
151+
assert m.__pydantic_extra__ == {'y': 2}
152+
```
153+
154+
1. The `= Field(init=False)` does not have any effect at runtime, but prevents the `__pydantic_extra__` field from
155+
being included as a parameter to the model's `__init__` method by type checkers.
156+
'''
125157

126158
frozen: bool
127159
"""

0 commit comments

Comments
 (0)
0