Repository: pydantic/pydantic

Issue:
"values" parameter in validator receives ambiguous types
### 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

# Bug

Output of `python -c "import pydantic.utils; print(pydantic.utils.version_info())"`:
```
python -c "import pydantic.utils; print(pydantic.utils.version_info())"                                          
             pydantic version: 1.6.1
            pydantic compiled: True
                 install path: /homes/<redacted>/.conda/envs/<redacted>/lib/python3.7/site-packages/pydantic
               python version: 3.7.8 | packaged by conda-forge | (default, Jul 31 2020, 02:25:08)  [GCC 7.5.0]
                     platform: Linux-4.9.0-0.bpo.6-amd64-x86_64-with-debian-8.11
     optional deps. installed: ['typing-extensions']
```

Hi,
In a validator method, when adding the `values` parameter, I expect it to be a map from field names to validated types.
When specifying `validate_assignment = True` in model config, `values` gets a different type (just on assignment).
This is illustrated by the example below:

```py
import pydantic

class ModelOne(pydantic.BaseModel):
    a: int

class ModelTwo(pydantic.BaseModel):
    m: ModelOne
    b: int

    @pydantic.validator('b')
    def validate_b(cls, b, values):
        print(values)
        if 'm' in values:
            return b + values['m'].a  # this fails with AttributeError if values['m'] is a dict
        else:
            return b

    class Config:
        validate_assignment = True

model = ModelTwo(m=ModelOne(a=1), b=2)
#> {'m': ModelOne(a=1)}
model.b = 3
#> {'m': {'a': 1}}
```

As far as I can tell, this behavior is not documented, and I'm pretty sure it's not intended.

edit: created a PR
"values" parameter in validator receives ambiguous types
### 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

# Bug

Output of `python -c "import pydantic.utils; print(pydantic.utils.version_info())"`:
```
python -c "import pydantic.utils; print(pydantic.utils.version_info())"                                          
             pydantic version: 1.6.1
            pydantic compiled: True
                 install path: /homes/<redacted>/.conda/envs/<redacted>/lib/python3.7/site-packages/pydantic
               python version: 3.7.8 | packaged by conda-forge | (default, Jul 31 2020, 02:25:08)  [GCC 7.5.0]
                     platform: Linux-4.9.0-0.bpo.6-amd64-x86_64-with-debian-8.11
     optional deps. installed: ['typing-extensions']
```

Hi,
In a validator method, when adding the `values` parameter, I expect it to be a map from field names to validated types.
When specifying `validate_assignment = True` in model config, `values` gets a different type (just on assignment).
This is illustrated by the example below:

```py
import pydantic

class ModelOne(pydantic.BaseModel):
    a: int

class ModelTwo(pydantic.BaseModel):
    m: ModelOne
    b: int

    @pydantic.validator('b')
    def validate_b(cls, b, values):
        print(values)
        if 'm' in values:
            return b + values['m'].a  # this fails with AttributeError if values['m'] is a dict
        else:
            return b

    class Config:
        validate_assignment = True

model = ModelTwo(m=ModelOne(a=1), b=2)
#> {'m': ModelOne(a=1)}
model.b = 3
#> {'m': {'a': 1}}
```

As far as I can tell, this behavior is not documented, and I'm pretty sure it's not intended.

edit: created a PR


Discussion:

Resolving patch:
diff --git a/pydantic/main.py b/pydantic/main.py
--- a/pydantic/main.py
+++ b/pydantic/main.py
@@ -367,8 +367,10 @@ def __setattr__(self, name, value):
         elif self.__config__.validate_assignment:
             known_field = self.__fields__.get(name, None)
             if known_field:
-                value, error_ = known_field.validate(value, self.dict(exclude={name}), loc=name, cls=self.__class__)
+                original_value = self.__dict__.pop(name)
+                value, error_ = known_field.validate(value, self.__dict__, loc=name, cls=self.__class__)
                 if error_:
+                    self.__dict__[name] = original_value
                     raise ValidationError([error_], self.__class__)
         self.__dict__[name] = value
         self.__fields_set__.add(name)


Test patch:
diff --git a/tests/test_validators.py b/tests/test_validators.py
--- a/tests/test_validators.py
+++ b/tests/test_validators.py
@@ -185,6 +185,30 @@ def test_validating_assignment_dict():
     ]
 
 
+def test_validating_assignment_values_dict():
+    class ModelOne(BaseModel):
+        a: int
+
+    class ModelTwo(BaseModel):
+        m: ModelOne
+        b: int
+
+        @validator('b')
+        def validate_b(cls, b, values):
+            if 'm' in values:
+                return b + values['m'].a  # this fails if values['m'] is a dict
+            else:
+                return b
+
+        class Config:
+            validate_assignment = True
+
+    model = ModelTwo(m=ModelOne(a=1), b=2)
+    assert model.b == 3
+    model.b = 3
+    assert model.b == 4
+
+
 def test_validate_multiple():
     # also test TypeError
     class Model(BaseModel):