Repository: pydantic/pydantic

Issue:
Required optional field has a default value of None after v1.7.0
### Checks

* [x] I added a descriptive title to this issue
* [x] I have searched (google, github) for similar issues and couldn't find anything
* [x] I have read and followed [the docs](https://pydantic-docs.helpmanual.io/) and still think this is a bug

<!-- Sorry to sound so draconian, but every second saved replying to issues is time spend improving pydantic :-) -->

# Bug

Output of `python -c "import pydantic.utils; print(pydantic.utils.version_info())"`:
```
             pydantic version: 1.7.2
            pydantic compiled: False
                 install path: /home/leovp/Projects/pydantic/pydantic
               python version: 3.6.12 (default, Sep  5 2020, 11:22:16)  [GCC 7.5.0]
                     platform: Linux-4.15.0-54-generic-x86_64-with-debian-buster-sid
     optional deps. installed: []
```
<!-- or if you're using pydantic prior to v1.3, manually include: OS, python version and pydantic version -->

<!-- Please read the [docs](https://pydantic-docs.helpmanual.io/) and search through issues to
confirm your bug hasn't already been reported. -->

<!-- Where possible please include a self-contained code snippet describing your bug: -->

```py
from typing import Optional

from pydantic import BaseModel


class Model(BaseModel):
    foo: int
    bar: Optional[int] = ...


m = Model(foo=1, bar=None)  # or Model(foo=1)
Model.parse_obj(m.dict(exclude_defaults=True))  # fails on pydantic v1.7.0+
```

It seems that `pydantic` thinks `bar` (a "required optional" field) has a default value of None and serializes the model without this key, which makes it non-deserializable. This was not the case on versions 1.5.x and 1.6.x, so I think it's a regression. (Or maybe it was never a guarantee).

According to `git bisect` the potential regression was introduced in commit 4bc4230df6294c62667fc3779328019a28912147.


Comments:
https://github.com/samuelcolvin/pydantic/blob/31bc2435d7968fdf8d1f0c5c67f0f851c1bef54e/pydantic/main.py#L789

Alright, I haven't looked really closely, but here's my guess: `getattr` here can never fallback to `_missing`, since `ModelField.default` is always there and has a value of `None` unless set explicitly:
https://github.com/samuelcolvin/pydantic/blob/31bc2435d7968fdf8d1f0c5c67f0f851c1bef54e/pydantic/fields.py#L255

UPD1: And here's where "bar" is created and gets the default value of `None`: https://github.com/samuelcolvin/pydantic/blob/31bc2435d7968fdf8d1f0c5c67f0f851c1bef54e/pydantic/fields.py#L308-L310

UPD2: Is it a good idea to check `field.field_info.default` instead of `field.default`? In this particular case it has a correct value of `Ellipsis`, and you could special-case it while serialising the model.
Hello @leovp and thanks for reporting :)
You're absolutely right! IMO the good fix would be to set `default = Ellipsis` when `required`
Would you like to make a PR for this? If you want I can do it as well tonight for a `v1.7.3`.
Please tell me.
Cheers
@PrettyWood 
It's 11PM where I live, so please go ahead :)

Patch:
diff --git a/pydantic/fields.py b/pydantic/fields.py
--- a/pydantic/fields.py
+++ b/pydantic/fields.py
@@ -307,7 +307,7 @@ def infer(
         required: 'BoolUndefined' = Undefined
         if value is Required:
             required = True
-            value = None
+            value = Ellipsis
         elif value is not Undefined:
             required = False
         field_info.alias = field_info.alias or field_info_from_config.get('alias')


Test patch:
diff --git a/tests/test_dataclasses.py b/tests/test_dataclasses.py
--- a/tests/test_dataclasses.py
+++ b/tests/test_dataclasses.py
@@ -406,7 +406,7 @@ class User:
     fields = user.__pydantic_model__.__fields__
 
     assert fields['id'].required is True
-    assert fields['id'].default is None
+    assert fields['id'].default is Ellipsis
 
     assert fields['name'].required is False
     assert fields['name'].default == 'John Doe'
@@ -425,7 +425,7 @@ class User:
     fields = user.__pydantic_model__.__fields__
 
     assert fields['id'].required is True
-    assert fields['id'].default is None
+    assert fields['id'].default is Ellipsis
 
     assert fields['aliases'].required is False
     assert fields['aliases'].default == {'John': 'Joey'}
diff --git a/tests/test_main.py b/tests/test_main.py
--- a/tests/test_main.py
+++ b/tests/test_main.py
@@ -958,6 +958,35 @@ class Config:
     assert m.dict(exclude_unset=True, by_alias=True) == {'alias_a': 'a', 'c': 'c'}
 
 
+def test_exclude_defaults():
+    class Model(BaseModel):
+        mandatory: str
+        nullable_mandatory: Optional[str] = ...
+        facultative: str = 'x'
+        nullable_facultative: Optional[str] = None
+
+    m = Model(mandatory='a', nullable_mandatory=None)
+    assert m.dict(exclude_defaults=True) == {
+        'mandatory': 'a',
+        'nullable_mandatory': None,
+    }
+
+    m = Model(mandatory='a', nullable_mandatory=None, facultative='y', nullable_facultative=None)
+    assert m.dict(exclude_defaults=True) == {
+        'mandatory': 'a',
+        'nullable_mandatory': None,
+        'facultative': 'y',
+    }
+
+    m = Model(mandatory='a', nullable_mandatory=None, facultative='y', nullable_facultative='z')
+    assert m.dict(exclude_defaults=True) == {
+        'mandatory': 'a',
+        'nullable_mandatory': None,
+        'facultative': 'y',
+        'nullable_facultative': 'z',
+    }
+
+
 def test_dir_fields():
     class MyModel(BaseModel):
         attribute_a: int