Repository: pydantic/pydantic

Issue:
dataclass field doesn't change required schema
# Bug

Please complete:
* OS: Mac OS
* Python version `import sys; print(sys.version)`: **3.6.7** with dataclasses backport
* Pydantic version `import pydantic; print(pydantic.VERSION)`: **1.0**

It is known that `@pydantic.dataclasses.dataclass` doesn't fully mimic the `BaseModel`. However, we'd like use `.schema()` representation faithfully. I'm wondering what's the best workaround.

Adding a `Config` for `b` with `required=False` doesn't carry over to `.schema()` either.

```py
from typing import Dict

from dataclasses import field
from pydantic import Field, ValidationError
from pydantic.dataclasses import dataclass

@dataclass
class D1:
    a: int
    # b: Dict[str, int] = {"x":10} # mutable default, not allowed
@dataclass
class D2:
    a: int
    b: Dict[str, int] = Field({"x":10}) # pydantic.Field, unsafe and invalid default
@dataclass        
class D3:
    a: int
    b: Dict[str, int] = field(default_factory=lambda: {"x":10}) # std lib, safe and invalid default
        
class C:
    fields = {"b": { "required": False}} # doesn't carry over!!

@dataclass(config=C)        
class D4:
    a: int
    b: Dict[str, int] = field(default_factory=lambda: {"x":10})

print(D1(10))
try:
    print(D2(10))
except ValidationError as e:
    print(e) # pydantic/dataclasses.py:77

print(D3(10))
req = D3.__pydantic_model__.schema()["required"]
if req != ["a"]:
    print('only ["a"] should be required, is:', req)
    
print()
print(D4.__pydantic_model__.schema()["required"]) # same as D3
```

Output
```
D1(a=10)
1 validation error for D2
b
  value is not a valid dict (type=type_error.dict)
D3(a=10, b={'x': 10})
only ["a"] should be required, is: ['a', 'b']

['a', 'b']
```


Discussion:
This isn't a bug, but a feature request.

```py
class Config:
    fields = {"b": { "required": False}}
```

Wouldn't work on a normal model.

I think what you want is better support for the `field()` function from dataclasses. If so, please describe exactly what parts of `field()` usage you'd like to support in pydantic. Is it just the default in schema?
Thanks for clarifying!
Only handling `default*` in schema is what we need to support. However, it would be nice to have a stable interface for `.__pydantic_model__`. I'm not sure if this might change. 
`__pydantic_model__` won't change, we should document it.

It would be wonderful if you could create a PR for that. :smile: 
i'm going for it

Resolving patch:
diff --git a/docs/examples/dataclasses_default_schema.py b/docs/examples/dataclasses_default_schema.py
new file mode 100644
--- /dev/null
+++ b/docs/examples/dataclasses_default_schema.py
@@ -0,0 +1,12 @@
+import dataclasses
+from typing import List
+from pydantic.dataclasses import dataclass
+
+@dataclass
+class User:
+    id: int
+    name: str = 'John Doe'
+    friends: List[int] = dataclasses.field(default_factory=lambda: [0])
+
+user = User(id='42')
+print(user.__pydantic_model__.schema())
diff --git a/pydantic/dataclasses.py b/pydantic/dataclasses.py
--- a/pydantic/dataclasses.py
+++ b/pydantic/dataclasses.py
@@ -83,10 +83,18 @@ def _pydantic_post_init(self: 'DataclassType', *initvars: Any) -> None:
     _cls.__post_init__ = _pydantic_post_init
     cls = dataclasses._process_class(_cls, init, repr, eq, order, unsafe_hash, frozen)  # type: ignore
 
-    fields: Dict[str, Any] = {
-        field.name: (field.type, field.default if field.default != dataclasses.MISSING else Required)
-        for field in dataclasses.fields(cls)
-    }
+    fields: Dict[str, Any] = {}
+    for field in dataclasses.fields(cls):
+
+        if field.default != dataclasses.MISSING:
+            field_value = field.default
+        # mypy issue 7020 and 708
+        elif field.default_factory != dataclasses.MISSING:  # type: ignore
+            field_value = field.default_factory()  # type: ignore
+        else:
+            field_value = Required
+
+        fields[field.name] = (field.type, field_value)
 
     validators = gather_all_validators(cls)
     cls.__pydantic_model__ = create_model(


Test patch:
diff --git a/tests/test_dataclasses.py b/tests/test_dataclasses.py
--- a/tests/test_dataclasses.py
+++ b/tests/test_dataclasses.py
@@ -1,7 +1,7 @@
 import dataclasses
 from datetime import datetime
 from pathlib import Path
-from typing import ClassVar, FrozenSet, Optional
+from typing import ClassVar, Dict, FrozenSet, Optional
 
 import pytest
 
@@ -403,11 +403,28 @@ class User:
     assert fields['signup_ts'].default is None
 
 
+def test_default_factory_field():
+    @pydantic.dataclasses.dataclass
+    class User:
+        id: int
+        aliases: Dict[str, str] = dataclasses.field(default_factory=lambda: {'John': 'Joey'})
+
+    user = User(id=123)
+    fields = user.__pydantic_model__.__fields__
+
+    assert fields['id'].required is True
+    assert fields['id'].default is None
+
+    assert fields['aliases'].required is False
+    assert fields['aliases'].default == {'John': 'Joey'}
+
+
 def test_schema():
     @pydantic.dataclasses.dataclass
     class User:
         id: int
         name: str = 'John Doe'
+        aliases: Dict[str, str] = dataclasses.field(default_factory=lambda: {'John': 'Joey'})
         signup_ts: datetime = None
 
     user = User(id=123)
@@ -417,6 +434,12 @@ class User:
         'properties': {
             'id': {'title': 'Id', 'type': 'integer'},
             'name': {'title': 'Name', 'default': 'John Doe', 'type': 'string'},
+            'aliases': {
+                'title': 'Aliases',
+                'default': {'John': 'Joey'},
+                'type': 'object',
+                'additionalProperties': {'type': 'string'},
+            },
             'signup_ts': {'title': 'Signup Ts', 'type': 'string', 'format': 'date-time'},
         },
         'required': ['id'],