Repository: pydantic/pydantic

Issue:
Simplify setting dynamic default values
# Feature Request

Please complete:
* OS: Windows
* Python version 3.7.4 (default, Aug  9 2019, 18:34:13) [MSC v.1915 64 bit (AMD64)]
* Pydantic version: 0.30

# Feature Request

Currently, if I want to have a dynamic default value, it has to be done the following way:

```py
from datetime import datetime

from pydantic import BaseModel, validator
from uuid import UUID, uuid4

class DemoModel(BaseModel):
    ts: datetime = None
    id: UUID = None

    @validator('id', pre=True, always=True)
    def set_id(cls, v):
        return v or uuid4()

    @validator('ts', pre=True, always=True)
    def set_ts_now(cls, v):
        return v or datetime.now()
```

This is fine for cases where actual validation has to be provided, however, in many cases like the one portrayed below, this leads to unnecessary code, especially if there are multiple dynamically set values. For instance, a timestamp and an id.

It would be great to be able to replace this usage pattern by a pydantic field called `Dynamic` for instance such that one can pass a callable as a default value. This would look like this:

```py
from datetime import datetime
from uuid import UUID, uuid4

from pydantic import BaseModel, validator, Dynamic

class DemoModel(BaseModel):
    ts: Dynamic[datetime] = datetime.now
    id: Dynamic[UUID] = uuid4
```

I don't expect that this would need to modify any existing functionalities. Probably just a need to create some sort of wrapper.



Comments:
I'm hesitant about pseudo types, without reading the docs it's not clear what this does.

What we could do is just is the function as the default/initial value? Then unless the type of the field was `Callable` it would call the function to generate a value.

This would not be completely backwards, but I think it would be more intuitive. 
This would work as well! The main idea would be to reduce the amount of boilerplate code required for this type of usage.
I think this might be better suited as a keyword argument to Schema (or Field now), or similar. That’s how this functionality is handled by both attrs and dataclasses.
> I think this might be better suited as a keyword argument to Schema (or Field now), or similar. That’s how this functionality is handled by both attrs and dataclasses.

agreed.
#1210 will implement `Field(default_factory=...)`.

For use of `ts: datetime = datetime.now` we should wait for v2.

Patch:
diff --git a/docs/examples/models_default_factory.py b/docs/examples/models_default_factory.py
new file mode 100644
--- /dev/null
+++ b/docs/examples/models_default_factory.py
@@ -0,0 +1,9 @@
+from pydantic import BaseModel, Field
+from uuid import UUID, uuid4
+
+class Model(BaseModel):
+    uid: UUID = Field(default_factory=uuid4)
+
+m1 = Model()
+m2 = Model()
+assert m1.uid != m2.uid
diff --git a/pydantic/fields.py b/pydantic/fields.py
--- a/pydantic/fields.py
+++ b/pydantic/fields.py
@@ -1,5 +1,6 @@
 import warnings
 from collections.abc import Iterable as CollectionsIterable
+from copy import deepcopy
 from typing import (
     TYPE_CHECKING,
     Any,
@@ -29,6 +30,7 @@
     AnyType,
     Callable,
     ForwardRef,
+    NoArgAnyCallable,
     NoneType,
     display_as_type,
     is_literal_type,
@@ -67,6 +69,7 @@ class FieldInfo(Representation):
 
     __slots__ = (
         'default',
+        'default_factory',
         'alias',
         'alias_priority',
         'title',
@@ -85,8 +88,9 @@ class FieldInfo(Representation):
         'extra',
     )
 
-    def __init__(self, default: Any, **kwargs: Any) -> None:
+    def __init__(self, default: Any = Undefined, **kwargs: Any) -> None:
         self.default = default
+        self.default_factory = kwargs.pop('default_factory', None)
         self.alias = kwargs.pop('alias', None)
         self.alias_priority = kwargs.pop('alias_priority', 2 if self.alias else None)
         self.title = kwargs.pop('title', None)
@@ -106,8 +110,9 @@ def __init__(self, default: Any, **kwargs: Any) -> None:
 
 
 def Field(
-    default: Any,
+    default: Any = Undefined,
     *,
+    default_factory: Optional[NoArgAnyCallable] = None,
     alias: str = None,
     title: str = None,
     description: str = None,
@@ -130,6 +135,8 @@ def Field(
 
     :param default: since this is replacing the field’s default, its first argument is used
       to set the default, use ellipsis (``...``) to indicate the field is required
+    :param default_factory: callable that will be called when a default value is needed for this field
+      If both `default` and `default_factory` are set, an error is raised.
     :param alias: the public name of the field
     :param title: can be any string, used in the schema
     :param description: can be any string, used in the schema
@@ -152,8 +159,12 @@ def Field(
       pattern string. The schema will have a ``pattern`` validation keyword
     :param **extra: any additional keyword arguments will be added as is to the schema
     """
+    if default is not Undefined and default_factory is not None:
+        raise ValueError('cannot specify both default and default_factory')
+
     return FieldInfo(
         default,
+        default_factory=default_factory,
         alias=alias,
         title=title,
         description=description,
@@ -208,6 +219,7 @@ class ModelField(Representation):
         'pre_validators',
         'post_validators',
         'default',
+        'default_factory',
         'required',
         'model_config',
         'name',
@@ -229,6 +241,7 @@ def __init__(
         class_validators: Optional[Dict[str, Validator]],
         model_config: Type['BaseConfig'],
         default: Any = None,
+        default_factory: Optional[NoArgAnyCallable] = None,
         required: 'BoolUndefined' = Undefined,
         alias: str = None,
         field_info: Optional[FieldInfo] = None,
@@ -241,6 +254,7 @@ def __init__(
         self.outer_type_: Any = type_
         self.class_validators = class_validators or {}
         self.default: Any = default
+        self.default_factory: Optional[NoArgAnyCallable] = default_factory
         self.required: 'BoolUndefined' = required
         self.model_config = model_config
         self.field_info: FieldInfo = field_info or FieldInfo(default)
@@ -257,6 +271,16 @@ def __init__(
         self.model_config.prepare_field(self)
         self.prepare()
 
+    def get_default(self) -> Any:
+        if self.default_factory is not None:
+            value = self.default_factory()
+        elif self.default is None:
+            # deepcopy is quite slow on None
+            value = None
+        else:
+            value = deepcopy(self.default)
+        return value
+
     @classmethod
     def infer(
         cls,
@@ -272,13 +296,13 @@ def infer(
 
         if isinstance(value, FieldInfo):
             field_info = value
-            value = field_info.default
+            value = field_info.default_factory() if field_info.default_factory is not None else field_info.default
         else:
             field_info = FieldInfo(value, **field_info_from_config)
         required: 'BoolUndefined' = Undefined
         if value is Required:
             required = True
-            value = None
+            field_info.default = None
         elif value is not Undefined:
             required = False
         field_info.alias = field_info.alias or field_info_from_config.get('alias')
@@ -288,7 +312,8 @@ def infer(
             type_=annotation,
             alias=field_info.alias,
             class_validators=class_validators,
-            default=value,
+            default=field_info.default,
+            default_factory=field_info.default_factory,
             required=required,
             model_config=config,
             field_info=field_info,
@@ -316,8 +341,9 @@ def prepare(self) -> None:
         Note: this method is **not** idempotent (because _type_analysis is not idempotent),
         e.g. calling it it multiple times may modify the field and configure it incorrectly.
         """
-        if self.default is not None and self.type_ is None:
-            self.type_ = type(self.default)
+        default_value = self.get_default()
+        if default_value is not None and self.type_ is None:
+            self.type_ = type(default_value)
             self.outer_type_ = self.type_
 
         if self.type_ is None:
@@ -332,14 +358,14 @@ def prepare(self) -> None:
             v.always for v in self.class_validators.values()
         )
 
-        if self.required is False and self.default is None:
+        if self.required is False and default_value is None:
             self.allow_none = True
 
         self._type_analysis()
         if self.required is Undefined:
             self.required = True
             self.field_info.default = Required
-        if self.default is Undefined:
+        if self.default is Undefined and self.default_factory is None:
             self.default = None
         self.populate_validators()
 
@@ -726,7 +752,10 @@ def __repr_args__(self) -> 'ReprArgs':
         args = [('name', self.name), ('type', self._type_display()), ('required', self.required)]
 
         if not self.required:
-            args.append(('default', self.default))
+            if self.default_factory is not None:
+                args.append(('default_factory', f'<function {self.default_factory.__name__}>'))
+            else:
+                args.append(('default', self.default))
 
         if self.alt_alias:
             args.append(('alias', self.alias))
diff --git a/pydantic/main.py b/pydantic/main.py
--- a/pydantic/main.py
+++ b/pydantic/main.py
@@ -853,11 +853,7 @@ def validate_model(  # noqa: C901 (ignore complexity)
                 errors.append(ErrorWrapper(MissingError(), loc=field.alias))
                 continue
 
-            if field.default is None:
-                # deepcopy is quite slow on None
-                value = None
-            else:
-                value = deepcopy(field.default)
+            value = field.get_default()
 
             if not config.validate_all and not field.validate_always:
                 values[name] = value
diff --git a/pydantic/typing.py b/pydantic/typing.py
--- a/pydantic/typing.py
+++ b/pydantic/typing.py
@@ -42,11 +42,13 @@ def evaluate_forwardref(type_: ForwardRef, globalns: Any, localns: Any) -> Type[
     from typing import Callable as Callable
 
     AnyCallable = Callable[..., Any]
+    NoArgAnyCallable = Callable[[], Any]
 else:
     from collections.abc import Callable as Callable
     from typing import Callable as TypingCallable
 
     AnyCallable = TypingCallable[..., Any]
+    NoArgAnyCallable = TypingCallable[[], Any]
 
 if sys.version_info < (3, 8):
     if TYPE_CHECKING:
@@ -77,6 +79,7 @@ def evaluate_forwardref(type_: ForwardRef, globalns: Any, localns: Any) -> Type[
     'ForwardRef',
     'Callable',
     'AnyCallable',
+    'NoArgAnyCallable',
     'AnyType',
     'NoneType',
     'display_as_type',


Test patch:
diff --git a/tests/test_main.py b/tests/test_main.py
--- a/tests/test_main.py
+++ b/tests/test_main.py
@@ -1,5 +1,6 @@
 from enum import Enum
-from typing import Any, ClassVar, List, Mapping, Type
+from typing import Any, Callable, ClassVar, List, Mapping, Type
+from uuid import UUID, uuid4
 
 import pytest
 
@@ -50,6 +51,28 @@ def test_ultra_simple_repr():
         assert m.to_string() == 'a=10.2 b=10'
 
 
+def test_default_dict_repr():
+    def myfunc():
+        return 1
+
+    class Model(BaseModel):
+        a: int = Field(default_factory=myfunc)
+        b = Field(default_factory=myfunc)
+
+    m = Model()
+    assert str(m) == 'a=1 b=1'
+    assert repr(m) == 'Model(a=1, b=1)'
+    assert (
+        repr(m.__fields__['a']) == "ModelField(name='a', type=int, required=False, default_factory='<function myfunc>')"
+    )
+    assert (
+        repr(m.__fields__['b']) == "ModelField(name='b', type=int, required=False, default_factory='<function myfunc>')"
+    )
+    assert dict(m) == {'a': 1, 'b': 1}
+    assert m.dict() == {'a': 1, 'b': 1}
+    assert m.json() == '{"a": 1, "b": 1}'
+
+
 def test_comparing():
     m = UltraSimpleModel(a=10.2, b='100')
     assert m == {'a': 10.2, 'b': 100}
@@ -970,3 +993,35 @@ class NewModel(DerivedModel, something=2):
         something = 1
 
     assert NewModel.something == 2
+
+
+def test_two_defaults():
+    with pytest.raises(ValueError, match='^cannot specify both default and default_factory$'):
+
+        class Model(BaseModel):
+            a: int = Field(default=3, default_factory=lambda: 3)
+
+
+def test_default_factory():
+    class ValueModel(BaseModel):
+        uid: UUID = uuid4()
+
+    m1 = ValueModel()
+    m2 = ValueModel()
+    assert m1.uid == m2.uid
+
+    class DynamicValueModel(BaseModel):
+        uid: UUID = Field(default_factory=uuid4)
+
+    m1 = DynamicValueModel()
+    m2 = DynamicValueModel()
+    assert isinstance(m1.uid, UUID)
+    assert m1.uid != m2.uid
+
+    # With a callable: we still should be able to set callables as defaults
+    class FunctionModel(BaseModel):
+        a: int = 1
+        uid: Callable[[], UUID] = Field(uuid4)
+
+    m = FunctionModel()
+    assert m.uid is uuid4