Pydantic: Dataclasses do not have a serialisation method

Created on 21 Jan 2019  路  5Comments  路  Source: samuelcolvin/pydantic


Feature Request

For bugs/questions:

  • OS: ubuntu 18.10
  • Python version import sys; print(sys.version): 3.7
  • Pydantic version import pydantic; print(pydantic.VERSION): from github master: 2fb0b26b81cab30641dc7d780021ee762ba1b076
from datetime import datetime
from pydantic.dataclasses import dataclass


@dataclass
class User:
    id: int
    name: str = 'John Doe'
    signup_ts: datetime = None


user = User(id='42', signup_ts='2032-06-21T12:00+00:00')
print(user)
print(user.dict())
(py37) 06:33:45 {master} ~/Downloads/pydantic$ python /tmp/test.py 
User(id=42, name='John Doe', signup_ts=datetime.datetime(2032, 6, 21, 12, 0, tzinfo=datetime.timezone.utc))
Traceback (most recent call last):
  File "/tmp/test.py", line 14, in <module>
    print(user.dict())
AttributeError: 'User' object has no attribute 'dict'

It is not clear how to serialise an object created as @dataclass It works fine if I use the pydantic.BaseModel subclass style, though.

Also, related question, I'm not sure what is the advantage of one style over the other. It's just confusing to have two ways of doing the same thing (especially if one of them is slightly broken).

question

Most helpful comment

If someone is still looking for this, the dataclasses module offers dc2dict in the form of asdict:

from dataclasses import dataclass, asdict

@dataclass
class InventoryItem:
    """Class for keeping track of an item in inventory."""
    name: str
    unit_price: float
    quantity_on_hand: int = 0

asdict(InventoryItem(name="A", unit_price=1.0))
#> {"name": "A", "unit_price": 1.0, "quantity_on_hand": 0}

All 5 comments

From the docs:

You can use all the standard pydantic field types and the resulting dataclass will be identical to the one created by the standard library dataclass decorator.

In other words, pydantic takes care of building a dataclass, how you use it is your concern.

It's easy enough to create a dict from a dataclass:

from dataclasses import dataclass

def dc2dict(dc):
    return {n: getattr(d, n) for n in dc.__dataclass_fields__}

@dataclass
class InventoryItem:
    """Class for keeping track of an item in inventory."""
    name: str
    unit_price: float
    quantity_on_hand: int = 0

item = InventoryItem(name='x', unit_price=42)
print(dc2dict(item))
#> {'name': 'x', 'unit_price': 42, 'quantity_on_hand': 0}

This isn't special to pydantic, it's standard dataclasses functionality.

Also, related question, I'm not sure what is the advantage of one style over the other.

  1. pydantic was started before python 3.7 and dataclasses, hence originally dataclasses weren't available
  2. Models have extra functionality not availabe in dataclasses eg. serialisation as you've found
  3. however some people understandably want to use dataclasses since they're a standard lib feature and very useful, hence pydantic developed support for them.

Well, it's a little more complicated than that in case there are other dataclasses inside the field values. But, fair enough.

Thanks!

True, I'd happily accept a PR to add a proper dataclass2dict method to pydantic and document it.

Maybe updating the docs on this issue would help clarify. Currently the docs read:

You can use all the standard pydantic field types and the resulting dataclass will be identical to the one created by the standard library dataclass decorator.

Updating this to be more explicit would be helpful:

You can use all the standard pydantic field types and the resulting dataclass will be identical to the one created by the standard library dataclass decorator. The resulting object will NOT have any pydantic serialization methods.

If someone is still looking for this, the dataclasses module offers dc2dict in the form of asdict:

from dataclasses import dataclass, asdict

@dataclass
class InventoryItem:
    """Class for keeping track of an item in inventory."""
    name: str
    unit_price: float
    quantity_on_hand: int = 0

asdict(InventoryItem(name="A", unit_price=1.0))
#> {"name": "A", "unit_price": 1.0, "quantity_on_hand": 0}
Was this page helpful?
0 / 5 - 0 ratings

Related issues

marlonjan picture marlonjan  路  37Comments

bradodarb picture bradodarb  路  22Comments

dmfigol picture dmfigol  路  38Comments

MrMrRobat picture MrMrRobat  路  22Comments

kryft picture kryft  路  35Comments