Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update documentation for generics support in V2 #6685

Merged
merged 2 commits into from Jul 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
27 changes: 27 additions & 0 deletions docs/usage/dataclasses.md
Expand Up @@ -167,6 +167,33 @@ print(navbar)

When used as fields, dataclasses (Pydantic or vanilla) should use dicts as validation inputs.

## Generic dataclasses

Pydantic supports generic dataclasses, including those with type variables.

```py
from typing import Generic, TypeVar

from pydantic import TypeAdapter
from pydantic.dataclasses import dataclass

T = TypeVar('T')


@dataclass
class GenericDataclass(Generic[T]):
x: T


validator = TypeAdapter(GenericDataclass)

assert validator.validate_python({'x': None}).x is None
assert validator.validate_python({'x': 1}).x == 1
assert validator.validate_python({'x': 'a'}).x == 'a'
```
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe worth noting that if you use the dataclass as a field of a BaseModel or via FastAPI you don't need a TypeAdapter


Note that, if you use the dataclass as a field of a `BaseModel` or via FastAPI you don't need a `TypeAdapter`.

## Stdlib dataclasses and Pydantic dataclasses

### Inherit from stdlib dataclasses
Expand Down
53 changes: 52 additions & 1 deletion docs/usage/models.md
Expand Up @@ -104,7 +104,8 @@ Models possess the following methods and attributes:
See [JSON Schema](json_schema.md).
* `model_parametrized_name()`: compute the class name for parametrizations of generic classes.
* `model_post_init()`: perform additional initialization after the model is initialized.
* `model_rebuild()`: rebuild the model schema.
* `model_rebuild()`: rebuild the model schema, which also supports building recursive generic models.
See [Rebuild model schema](#rebuild-model-schema).
* `model_validate()`: a utility for loading any object into a model with error handling if the object is not a
dictionary. See [Helper functions](#helper-functions).
* `model_validate_json()`: a utility for validating the given JSON data against the Pydantic model. See
Expand Down Expand Up @@ -158,6 +159,49 @@ print(m.model_dump())

For self-referencing models, see [postponed annotations](postponed_annotations.md#self-referencing-or-recursive-models).

## Rebuild model schema

The model schema can be rebuilt using `model_rebuild()`. This is useful for building recursive generic models.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to tell users when it's useful. Something like "we try to determine when this is necessary and error if it wasn't done but you may want to call model_rebuild() proactively when dealing with recursive and/or generics.


```py
from pydantic import BaseModel, PydanticUserError


class Foo(BaseModel):
x: 'Bar'


try:
Foo.model_json_schema()
except PydanticUserError as e:
print(e)
"""
`Foo` is not fully defined; you should define `Bar`, then call `Foo.model_rebuild()`.

For further information visit https://errors.pydantic.dev/2/u/class-not-fully-defined
"""


class Bar(BaseModel):
pass


Foo.model_rebuild()
print(Foo.model_json_schema())
"""
{
'$defs': {'Bar': {'properties': {}, 'title': 'Bar', 'type': 'object'}},
'properties': {'x': {'$ref': '#/$defs/Bar'}},
'required': ['x'],
'title': 'Foo',
'type': 'object',
}
"""
```

Pydantic tries to determine when this is necessary automatically and error if it wasn't done, but you may want to
call `model_rebuild()` proactively when dealing with recursive models or generics.

## Arbitrary class instances

(Formerly known as "ORM Mode"/`from_orm`.)
Expand Down Expand Up @@ -730,6 +774,13 @@ print(concrete_model(a=1, b=1))
#> a=1 b=1
```

!!! warning
While it may not raise an error, we strongly advise against using parametrized generics in isinstance checks.

For example, you should not do `isinstance(my_model, MyGenericModel[int])`. However, it is fine to do `isinstance(my_model, MyGenericModel)`. (Note that, for standard generics, it would raise an error to do a subclass check with a parameterized generic.)

If you need to perform isinstance checks against parametrized generics, you can do this by subclassing the parametrized generic class. This looks like `class MyIntModel(MyGenericModel[int]): ...` and `isinstance(my_model, MyIntModel)`.

If a Pydantic model is used in a `TypeVar` constraint, [`SerializeAsAny`](serialization.md#serializing-with-duck-typing) can be used to
serialize it using the concrete model instead of the model `TypeVar` is bound to.

Expand Down