mirror of
https://github.com/kennethreitz/pydantic.git
synced 2026-06-05 23:00:18 +00:00
385 lines
14 KiB
Markdown
385 lines
14 KiB
Markdown
The primary means of defining objects in *pydantic* is via models
|
|
(models are simply classes which inherit from `BaseModel`).
|
|
|
|
You can think of models as similar to types in strictly typed languages or the requirements of a single endpoint
|
|
in an API.
|
|
|
|
Untrusted data can be passed to a model, after parsing and validation *pydantic* guarantees that the fields
|
|
of the resultant model instance will conform to the field types defined on the model.
|
|
|
|
!!! note
|
|
*pydantic* is primarily a parsing library, **not a validation library**.
|
|
Validation is a means to an end - building a model which conforms to the types and constraints provided.
|
|
|
|
In other words *pydantic* guarantees the types and constraints of the output model, not the input data.
|
|
|
|
This might sound like an esoteric distinction, but it is not - you should read about
|
|
[Data Conversion](#data-conversion) if you're unsure what this means or how it might effect your usage.
|
|
|
|
## Basic model usage
|
|
|
|
```py
|
|
from pydantic import BaseModel
|
|
|
|
class User(BaseModel):
|
|
id: int
|
|
name = 'Jane Doe'
|
|
```
|
|
`User` here is a model with two fields `id` which is an integer and is required,
|
|
and `name` which is a string and is not required (it has a default value). The type of `name` is inferred from the
|
|
default value, thus a type annotation is not required (however note [this](#field-ordering) warning about field
|
|
order when some fields do not have type annotations).
|
|
```py
|
|
user = User(id='123')
|
|
```
|
|
`user` here is an instance of `User`. Initialisation of the object will perform all parsing and validation,
|
|
if no `ValidationError` is raised, you know the resulting model instance is valid.
|
|
```py
|
|
assert user.id == 123
|
|
```
|
|
fields of a model can be accessed as normal attributes of the user object
|
|
the string '123' has been cast to an int as per the field type
|
|
```py
|
|
assert user.name == 'Jane Doe'
|
|
```
|
|
name wasn't set when user was initialised, so it has the default value
|
|
```py
|
|
assert user.__fields_set__ == {'id'}
|
|
```
|
|
the fields which were supplied when user was initialised:
|
|
```py
|
|
assert user.dict() == dict(user) == {'id': 123, 'name': 'Jane Doe'}
|
|
```
|
|
either `.dict()` or `dict(user)` will provide a dict of fields, but `.dict()` can take numerous other arguments.
|
|
```py
|
|
user.id = 321
|
|
assert user.id == 321
|
|
```
|
|
This model is mutable so field values can be changed.
|
|
|
|
### Model properties
|
|
|
|
The example above only shows the tip of the iceberg of what models can do.
|
|
Models contains the following methods and attributes:
|
|
|
|
`dict()`
|
|
: returns a dictionary of the model's fields and values,
|
|
see [exporting models](exporting_models.md#modeldict) for more details
|
|
|
|
`json()`
|
|
: returns a JSON string representation `dict()`,
|
|
see [exporting models](exporting_models.md#modeljson) for more details
|
|
|
|
`copy()`
|
|
: returns a deep copy of the model, see [exporting models](exporting_models.md#modeldcopy) for more details
|
|
|
|
`parse_obj()`
|
|
: utility for loading any object into a model with error handling if the object is not a dictionary,
|
|
see [helper function below](#helper-functions)
|
|
|
|
`parse_raw()`
|
|
: utility for loading strings of numerous formats, see [helper function below](#helper-functions)
|
|
|
|
`parse_file()`
|
|
: like `parse_raw()` but for files, see [helper function below](#helper-functions)
|
|
|
|
`from_orm()`
|
|
: for loading data from arbitrary classes, see [ORM mode below](#orm-mode-aka-arbitrary-class-instances)
|
|
|
|
`schema()`
|
|
: returns a dictionary representing the model as JSON Schema, see [Schema](schema.md)
|
|
|
|
`schema_json()`
|
|
: returns a JSON string representation `schema()`, see [Schema](schema.md)
|
|
|
|
`fields`
|
|
: a dictionary of the model class's fields
|
|
|
|
`__config__`
|
|
: the configuration class for this model, see [model config](model_config.md)
|
|
|
|
`__fields_set__`
|
|
: Set of names of fields which were set when the model instance was initialised
|
|
|
|
## Recursive Models
|
|
|
|
More complex hierarchical data structures can be defined using models as types in annotations themselves.
|
|
|
|
```py
|
|
{!./examples/recursive.py!}
|
|
```
|
|
_(This script is complete, it should run "as is")_
|
|
|
|
For self-referencing models, see [postponed annotations](postponed_annotations.md#self-referencing-models).
|
|
|
|
## ORM Mode (aka Arbitrary Class Instances)
|
|
|
|
Pydantic models can be created from arbitrary class instances to support models that map to ORM objects.
|
|
|
|
To do this:
|
|
1. The [Config](model_config.md) property `orm_mode` must be set to `True`.
|
|
2. The special constructor `from_orm` must be used to create the model instance.
|
|
|
|
The example here uses SQLAlchemy but the same approach should work for any ORM.
|
|
|
|
```py
|
|
{!./examples/orm_mode.py!}
|
|
```
|
|
_(This script is complete, it should run "as is")_
|
|
|
|
ORM instances will be parsed with `from_orm` recursively as well as at the top level.
|
|
|
|
Here a vanilla class is used to demonstrate the principle, but any ORM could be used instead.
|
|
|
|
```py
|
|
{!./examples/orm_mode_recursive.py!}
|
|
```
|
|
_(This script is complete, it should run "as is")_
|
|
|
|
Arbitrary classes are processed by *pydantic* using the `GetterDict` class
|
|
(see [utils.py](https://github.com/samuelcolvin/pydantic/blob/master/pydantic/utils.py)) which attempts to
|
|
provide a dictionary-like interface to any class. You can customise how this works by setting your own
|
|
sub-class of `GetterDict` in `Config.getter_dict` (see [config](model_config.md)).
|
|
|
|
You can also customise class validation using [root_validators](validators.md#root-validators) with `pre=True`,
|
|
in this case your validator function will be passed a `GetterDict` instance which you may copy and modify.
|
|
|
|
## Error Handling
|
|
|
|
*pydantic* will raise `ValidationError` whenever it finds an error in the data it's validating.
|
|
|
|
!!! note
|
|
Validation code should not raise `ValidationError` itself, but rather raise `ValueError`, `TypeError` or
|
|
`AssertionError` (or subclasses of `ValueError` or `TypeError`) which will be caught and used to populate
|
|
`ValidationError`.
|
|
|
|
One exception will be raised regardless of the number of errors found, that `ValidationError` will
|
|
contain information about all the errors and how they happened.
|
|
|
|
You can access these errors in a several ways:
|
|
|
|
`e.errors()`
|
|
: method will return list of errors found in the input data.
|
|
|
|
`e.json()`
|
|
: method will return a JSON representation of `errors`.
|
|
|
|
`str(e)`
|
|
: method will return a human readable representation of the errors.
|
|
|
|
Each error object contains:
|
|
|
|
`loc`
|
|
: the error's location as a list, the first item in the list will be the field where the error occurred,
|
|
subsequent items will represent the field where the error occurred
|
|
in [sub models](models.md#recursive_models) when they're used.
|
|
|
|
`type`
|
|
: a unique identifier of the error readable by a computer.
|
|
|
|
`msg`
|
|
: a human readable explanation of the error.
|
|
|
|
`ctx`
|
|
: an optional object which contains values required to render the error message.
|
|
|
|
To demonstrate that:
|
|
|
|
```py
|
|
{!./examples/errors1.py!}
|
|
```
|
|
_(This script is complete, it should run "as is". `json()` has `indent=2` set by default, but I've tweaked the
|
|
JSON here and below to make it slightly more concise.)_
|
|
|
|
### Custom Errors
|
|
|
|
In your custom data types or validators you should use `ValueError`, `TypeError` or `AssertionError` to raise errors.
|
|
|
|
See [validators](validators.md) for more details on use of the `@validator` decorator.
|
|
|
|
```py
|
|
{!./examples/errors2.py!}
|
|
```
|
|
_(This script is complete, it should run "as is")_
|
|
|
|
You can also define your own error class with abilities to specify custom error code, message template and context:
|
|
|
|
```py
|
|
{!./examples/errors3.py!}
|
|
```
|
|
_(This script is complete, it should run "as is")_
|
|
|
|
## Helper Functions
|
|
|
|
*Pydantic* provides three `classmethod` helper functions on models for parsing data:
|
|
|
|
* **`parse_obj`** this is almost identical to the `__init__` method of the model except if the object passed is not
|
|
a dict `ValidationError` will be raised (rather than python raising a `TypeError`).
|
|
* **`parse_raw`** takes a *str* or *bytes* parses it as *json*, or *pickle* data and then passes
|
|
the result to `parse_obj`. The data type is inferred from the `content_type` argument,
|
|
otherwise *json* is assumed.
|
|
* **`parse_file`** reads a file and passes the contents to `parse_raw`, if `content_type` is omitted it is inferred
|
|
from the file's extension.
|
|
|
|
```py
|
|
{!./examples/parse.py!}
|
|
```
|
|
_(This script is complete, it should run "as is")_
|
|
|
|
!!! note
|
|
Since `pickle` allows complex objects to be encoded, to use it you need to explicitly pass `allow_pickle` to
|
|
the parsing function.
|
|
|
|
## Generic Models
|
|
|
|
!!! note
|
|
New in version **v0.29**.
|
|
|
|
This feature requires Python 3.7+.
|
|
|
|
Pydantic supports the creation of generic models to make it easier to reuse a common model structure.
|
|
|
|
In order to declare a generic model, you perform the following steps:
|
|
|
|
* Declare one or more `typing.TypeVar` instances to use to parameterize your model.
|
|
* Declare a pydantic model that inherits from `pydantic.generics.GenericModel` and `typing.Generic`,
|
|
where you pass the `TypeVar` instances as parameters to `typing.Generic`.
|
|
* Use the `TypeVar` instances as annotations where you will want to replace them with other types or
|
|
pydantic models.
|
|
|
|
Here is an example using `GenericModel` to create an easily-reused HTTP response payload wrapper:
|
|
|
|
```py
|
|
{!./examples/generics.py!}
|
|
```
|
|
_(This script is complete, it should run "as is")_
|
|
|
|
If you set `Config` or make use of `validator` in your generic model definition, it is applied
|
|
to concrete subclasses in the same way as when inheriting from `BaseModel`. Any methods defined on
|
|
your generic class will also be inherited.
|
|
|
|
Pydantic's generics also integrate properly with mypy, so you get all the type checking
|
|
you would expect mypy to provide if you were to declare the type without using `GenericModel`.
|
|
|
|
!!! note
|
|
Internally, pydantic uses `create_model` to generate a (cached) concrete `BaseModel` at runtime,
|
|
so there is essentially zero overhead introduced by making use of `GenericModel`.
|
|
|
|
If the name of the concrete subclasses is important, you can also override the default behavior:
|
|
|
|
```py
|
|
{!./examples/generics-naming.py!}
|
|
```
|
|
_(This script is complete, it should run "as is")_
|
|
|
|
## Dynamic model creation
|
|
|
|
There are some occasions where the shape of a model is not known until runtime, for this *pydantic* provides
|
|
the `create_model` method to allow models to be created on the fly.
|
|
|
|
```py
|
|
{!./examples/dynamic_model_creation.py!}
|
|
```
|
|
|
|
Here `StaticFoobarModel` and `DynamicFoobarModel` are identical.
|
|
|
|
Fields are defined by either a a tuple of the form `(<type>, <default value>)` or just a default value. The
|
|
special key word arguments `__config__` and `__base__` can be used to customise the new model. This includes
|
|
extending a base model with extra fields.
|
|
|
|
```py
|
|
{!./examples/dynamic_inheritance.py!}
|
|
```
|
|
|
|
## Custom Root Types
|
|
|
|
Pydantic models which do not represent a `dict` ("object" in JSON parlance) can have a custom
|
|
root type defined via the `__root__` field. The root type can of any type: list, float, int etc.
|
|
|
|
The root type can be defined via the type hint on the `__root__` field.
|
|
The root value can be passed to model `__init__` via the `__root__` keyword argument or as
|
|
the first and only argument to `parse_obj`.
|
|
|
|
```py
|
|
{!examples/custom_root_field.py!}
|
|
```
|
|
|
|
## Faux Immutability
|
|
|
|
Models can be configured to be immutable via `allow_mutation = False` this will prevent changing attributes of
|
|
a model. See [model config](model_config.md) for more details on `Config`.
|
|
|
|
!!! warning
|
|
Immutability in python is never strict. If developers are determined/stupid they can always
|
|
modify a so-called "immutable" object.
|
|
|
|
```py
|
|
{!./examples/mutation.py!}
|
|
```
|
|
|
|
Trying to change `a` caused an error and it remains unchanged, however the dict `b` is mutable and the
|
|
immutability of `foobar` doesn't stop `b` from being changed.
|
|
|
|
## Abstract Base Classes
|
|
|
|
Pydantic models can be used alongside Python's
|
|
[Abstract Base Classes](https://docs.python.org/3/library/abc.html) (ABCs).
|
|
|
|
```py
|
|
{!./examples/ex_abc.py!}
|
|
```
|
|
_(This script is complete, it should run "as is")_
|
|
|
|
## Field Ordering
|
|
|
|
Field order is important in models for the following reason:
|
|
|
|
* validation is performed in the order fields are defined; [fields validators](validators.md)
|
|
can access the values of earlier fields, but not later ones
|
|
* field order is preserved in the model [schema](schema.md)
|
|
* field order is preserved in [validation errors](#error-handling)
|
|
* field order is preserved by [`.dict()` and `.json()` etc.](exporting_models.md#modeldict)
|
|
|
|
As of **v1.0** all fields with annotations (both annotation only and annotations with a value) come first followed
|
|
by fields with no annotation. Within each group fields remain in the order they were defined.
|
|
|
|
```py
|
|
{!./examples/field_order.py!}
|
|
```
|
|
_(This script is complete, it should run "as is")_
|
|
|
|
!!! warning
|
|
Note here that field order when both annotated and un-annotated fields are used is esoteric and not obvious
|
|
at first glance.
|
|
|
|
**In general therefore, it's preferable to add type annotations to all fields even when a default value
|
|
also defines the type.**
|
|
|
|
## Required fields
|
|
|
|
In addition to annotation only fields to denote required fields, an ellipsis (`...`) can be used as the value
|
|
|
|
```py
|
|
from pydantic import BaseModel
|
|
|
|
class Model(BaseModel):
|
|
a: int
|
|
b: int = ...
|
|
```
|
|
|
|
Here both `a` and `b` are required here. Use of ellipses for required fields does not work well with [mypy](mypy.md)
|
|
so should generally be avoided.
|
|
|
|
## Data Conversion
|
|
|
|
*pydantic* may cast input data to force it to conform model field types. This may result in information being lost, take
|
|
the following example:
|
|
|
|
```py
|
|
{!./examples/data_conversion.py!}
|
|
```
|
|
_(This script is complete, it should run "as is")_
|
|
|
|
This is a deliberate decision of *pydantic*, and in general it's the most useful approach, see
|
|
[here](https://github.com/samuelcolvin/pydantic/issues/578) for a longer discussion of the subject.
|