For bugs/questions:
I liked the idea of using a dataclass instead of subclassing from BaseModel, so I tried changing the very first example from the docs to use dataclass instead of BaseModel and it fails.
from datetime import datetime
from typing import List
# from pydantic import BaseModel
from pydantic.dataclasses import dataclass
# class User(BaseModel):
@dataclass
class User:
id: int
name = 'John Doe'
signup_ts: datetime = None
friends: List[int] = []
external_data = {'id': '123', 'signup_ts': '2017-06-01 12:22', 'friends': [1, '2', b'3']}
user = User(**external_data)
print(user)
# > User id=123 name='John Doe' signup_ts=datetime.datetime(2017, 6, 1, 12, 22) friends=[1, 2, 3]
print(user.id)
# > 123
Result:
Traceback (most recent call last):
File "my_pydantic_test.py", line 7, in <module>
@dataclass
File "pydantic/dataclasses.py", line 128, in pydantic.dataclasses.dataclass
# +-------+-------+-------+--------+--------+
File "pydantic/dataclasses.py", line 123, in pydantic.dataclasses.dataclass.wrap
# | | |
File "pydantic/dataclasses.py", line 77, in pydantic.dataclasses._process_class
# +--- frozen= parameter
File "/path/to/python/lib/python3.7/dataclasses.py", line 834, in _process_class
for name, type in cls_annotations.items()]
File "/path/to/python/lib/python3.7/dataclasses.py", line 834, in <listcomp>
for name, type in cls_annotations.items()]
File "/path/to/python/lib/python3.7/dataclasses.py", line 727, in _get_field
raise ValueError(f'mutable default {type(f.default)} for field '
ValueError: mutable default <class 'list'> for field friends is not allowed: use default_factory
I realize that this error is coming from the Std. Lib. dataclasses module, not pydantic. However, based on the language in the dataclasses section of the documentation I had expected what anything I could do with BaseModel I could do with dataclass as well.
If you don鈥檛 want to use pydantic鈥檚 BaseModel you can instead get the same data validation on standard dataclasses (introduced in python 3.7).
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.
Can I suggest that there be a note or warning to the user that there are certain restrictions associated with using a dataclass that are not present when using BaseModel (such as not being able to use mutable defaults, as well as #484 and #639)?
happy to accept a PR to improve the documentation.
hi @samuelcolvin, first just want to say thanks for such a nice tool! So the reason I believe the mutable fields are not allowed on regular data classes is due to them being shared (probably unexpectedly for most people) similar to how if you do this:
class Y(object):
def __init__(self,mutable=[]):
self._mutable = mutable
y1 = Y() # y1._mutable = []
y2 = Y() #聽y2._mutable = []
y1._mutable.append(1) # y1._mutable = [1], but surprise! y2._mutable = [1]
The Pydantic BaseModel does not seem to suffer from this:
class X(pydantic.BaseModel):
list_: List[int] = []
x1 = X() #聽x1.list_ = []
x2 = X() # x2.list_ = []
x1.list_.append(1) #聽x1.list_ = [1], x2.list_ = []
Have I understood that correctly? (Sorry if it's in the docs, I looked but couldn't find it specifically mentioned)
Is not a pydantic error, check https://docs.python.org/3/library/dataclasses.html#dataclasses.field
@leiserfg Yes, I tried to make that point very clear in the original issue with the text
I realize that this error is coming from the Std. Lib. dataclasses module, not pydantic.
The point of this issue is that the documentation makes it seem like you can get the same behavior inheriting from BaseModel as with using dataclass, but in corner cases like this that is not possible, and it should be documented as such. I do NOT think any pydantic behavior should be changed WRT to this issue.
I had another issue with dataclasses, they don't support extra fields even when you have Extra.ignore (the default), that's because the generated __init__ does not allow extra arguments, so I'm using BaseModel again.
We should make it clear that pydantic.dataclasses.dataclass is (mostly) a drop in replacement for dataclasses.dataclass with validation, not a replacement for pydantic.BaseModel.
Most helpful comment
I had another issue with dataclasses, they don't support extra fields even when you have
Extra.ignore(the default), that's because the generated__init__does not allow extra arguments, so I'm using BaseModel again.