Pydantic dataclasses provide validation without BaseModel
Pydantic dataclasses allow you to get the same data validation on standard dataclasses without using BaseModel. They are decorated with @pydantic.dataclasses.dataclass and support Pydantic validation on stdlib dataclass syntax.
Pydantic dataclasses are not a replacement for Pydantic models
Pydantic dataclasses provide similar functionality to stdlib dataclasses with Pydantic validation added, but they are not a complete replacement for Pydantic models. There are cases where using Pydantic models is the better choice.
Wrapping Pydantic dataclasses with TypeAdapter for validation and serialization
Since Pydantic dataclasses do not have the various methods to validate, dump, and generate JSON Schema that models have, you can wrap the dataclass with a TypeAdapter to access methods like dump_python() and validate_python().
Validators work with Pydantic dataclasses
You can use @field_validator decorators on Pydantic dataclasses. Validators are applied during initialization with the same modes (before, after) as with Pydantic models.
__post_init__() is called between before and after validators in Pydantic dataclasses
The dataclass __post_init__() method is supported in Pydantic dataclasses and will be called between the calls to before and after model validators, providing a predictable initialization order.
Generic dataclasses do not validate parameterized types
Generic Pydantic dataclasses are supported, but using a parameterized dataclass like Foo[int] will not perform validation for that specific type parameter. Unlike generic Pydantic models, parameterized dataclasses are generic aliases, not proper type objects. To work around this, wrap Foo[int] with TypeAdapter.
Pydantic dataclasses accept both Pydantic Field() and stdlib field()
You can use both pydantic.Field() and dataclasses.field() functions when defining fields in Pydantic dataclasses. This allows mixing Pydantic-specific features like constraints (ge, le) with stdlib dataclass features like default_factory and metadata.
Pydantic dataclasses can inherit from stdlib dataclasses
Stdlib dataclasses (nested or not) can be inherited by Pydantic dataclasses, and Pydantic will automatically validate all inherited fields. The @pydantic.dataclasses.dataclass decorator can also be applied directly to a stdlib dataclass to create a new subclass with Pydantic validation.
Stdlib dataclasses used in Pydantic models get validated
When a standard library dataclass is used as a field annotation in a Pydantic model, Pydantic dataclass, or TypeAdapter, Pydantic will apply validation. Using a stdlib or Pydantic dataclass as a field annotation is functionally equivalent in terms of validation.
is_pydantic_dataclass() function to check if a dataclass is from Pydantic
Use pydantic.dataclasses.is_pydantic_dataclass() to check if a type is specifically a Pydantic dataclass. The stdlib dataclasses.is_dataclass() will return True for both stdlib and Pydantic dataclasses, so is_pydantic_dataclass() is needed to distinguish them.
rebuild_dataclass() function to rebuild dataclass schema
The pydantic.dataclasses.rebuild_dataclass() function can be used to rebuild the core schema of a dataclass, similar to the rebuild_model() function for models.
Extra data behavior in Pydantic dataclasses
In Pydantic dataclasses, extra data is not included in serialization. Unlike Pydantic models, there is no way to customize validation of extra values using the __pydantic_extra__ attribute.
Example: Pydantic dataclass with validation
```python
from datetime import datetime
from pydantic.dataclasses import dataclass
@dataclass
class User:
id: int
name: str = 'John Doe'
signup_ts: datetime | None = None
user = User(id='42', signup_ts='2032-06-21T12:00')
print(user)
# User(id=42, name='John Doe', signup_ts=datetime.datetime(2032, 6, 21, 12, 0))
```
This shows Pydantic dataclasses automatically coercing string input to int and datetime types.
Example: Using TypeAdapter with Pydantic dataclasses
```python
from pydantic import TypeAdapter
from pydantic.dataclasses import dataclass
@dataclass
class Foo:
f: int
foo = Foo(f=1)
TypeAdapter(Foo).dump_python(foo) # {'f': 1}
TypeAdapter(Foo).validate_python({'f': 1}) # Foo(f=1)
```
This shows wrapping a dataclass with TypeAdapter to access validation and serialization methods.
Example: Field validators with Pydantic dataclasses
```python
from pydantic import field_validator
from pydantic.dataclasses import dataclass
@dataclass
class DemoDataclass:
product_id: str
@field_validator('product_id', mode='before')
@classmethod
def convert_int_serial(cls, v):
if isinstance(v, int):
v = str(v).zfill(5)
return v
print(DemoDataclass(product_id='01234')) # DemoDataclass(product_id='01234')
print(DemoDataclass(product_id=2468)) # DemoDataclass(product_id='02468')
```
This shows using @field_validator with Pydantic dataclasses.
Example: Inheritance from stdlib dataclasses
```python
import dataclasses
import pydantic
@dataclasses.dataclass
class Z:
z: int
@dataclasses.dataclass
class Y(Z):
y: int = 0
@pydantic.dataclasses.dataclass
class X(Y):
x: int = 0
foo = X(x=b'1', y='2', z='3')
print(foo) # X(z=3, y=2, x=1)
```
This shows Pydantic dataclasses validating inherited fields from stdlib dataclasses.
Example: __post_init__() execution order with validators
```python
from pydantic_core import ArgsKwargs
from typing_extensions import Self
from pydantic import model_validator
from pydantic.dataclasses import dataclass
@dataclass
class User:
birth: 'Birth'
@model_validator(mode='before')
@classmethod
def before(cls, values: ArgsKwargs) -> ArgsKwargs:
print(f'First: {values}')
return values
@model_validator(mode='after')
def after(self) -> Self:
print(f'Third: {self}')
return self
def __post_init__(self):
print(f'Second: {self.birth}')
user = User(**{'birth': {'year': 1995, 'month': 3, 'day': 2}})
```
This shows the execution order: before validator, then __post_init__(), then after validator. The values parameter in 'before' mode is ArgsKwargs, not a dict.