Repository: pydantic/pydantic

Issue:
`validate_assignment` changes the field order
### Checks

* [*] I added a descriptive title to this issue
* [*] I have searched (google, github) for similar issues and couldn't find anything
* [*] 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

```
             pydantic version: 1.7.1
            pydantic compiled: True
                 install path: /home/user/work/bugs/python/pydantic-ordering/.venv/lib/python3.8/site-packages/pydantic
               python version: 3.8.5 (default, Jul 28 2020, 12:59:40)  [GCC 9.3.0]
                     platform: Linux-4.19.128-microsoft-standard-x86_64-with-glibc2.29
     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: -->

Including `validate_assignment = True` in a model's config seems to cause assignment to change the field order when using `dict()`. This change was discovered when upgrading to 1.7.0 and also applies to 1.7.1, but was not present in prior versions.

This behaviour does not seem to agree with <https://pydantic-docs.helpmanual.io/usage/models/#field-ordering>, and is something that is important for my uses (where it generates massive diffs in the output) and surely others.

Running this snippet with and without the `validate_assignment` line produces different output:
```py
from pydantic import BaseModel

class Model(BaseModel):
    title: str = ''
    name: str = ''

    class Config:
        validate_assignment = True
        ...

m = Model(name='name')
m.title = 'title'
print(m.dict())
```




Discussion:

Resolving patch:
diff --git a/pydantic/main.py b/pydantic/main.py
--- a/pydantic/main.py
+++ b/pydantic/main.py
@@ -384,14 +384,14 @@ def __setattr__(self, name, value):  # noqa: C901 (ignore complexity)
 
             known_field = self.__fields__.get(name, None)
             if known_field:
-                original_value = self.__dict__.pop(name)
-                try:
-                    value, error_ = known_field.validate(value, self.__dict__, loc=name, cls=self.__class__)
-                    if error_:
-                        raise ValidationError([error_], self.__class__)
-                except Exception:
-                    self.__dict__[name] = original_value
-                    raise
+                # We want to
+                # - make sure validators are called without the current value for this field inside `values`
+                # - keep other values (e.g. submodels) untouched (using `BaseModel.dict()` will change them into dicts)
+                # - keep the order of the fields
+                dict_without_original_value = {k: v for k, v in self.__dict__.items() if k != name}
+                value, error_ = known_field.validate(value, dict_without_original_value, loc=name, cls=self.__class__)
+                if error_:
+                    raise ValidationError([error_], self.__class__)
                 else:
                     new_values[name] = value
 


Test patch:
diff --git a/tests/test_validators.py b/tests/test_validators.py
--- a/tests/test_validators.py
+++ b/tests/test_validators.py
@@ -5,7 +5,7 @@
 
 import pytest
 
-from pydantic import BaseModel, ConfigError, Extra, ValidationError, errors, validator
+from pydantic import BaseModel, ConfigError, Extra, Field, ValidationError, errors, validator
 from pydantic.class_validators import make_generic_validator, root_validator
 from pydantic.typing import Literal
 
@@ -1162,7 +1162,8 @@ def test_field_that_is_being_validated_is_excluded_from_validator_values(mocker)
 
     class Model(BaseModel):
         foo: str
-        bar: str
+        bar: str = Field(alias='pika')
+        baz: str
 
         class Config:
             validate_assignment = True
@@ -1170,12 +1171,27 @@ class Config:
         @validator('foo')
         def validate_foo(cls, v, values):
             check_values({**values})
+            return v
+
+        @validator('bar')
+        def validate_bar(cls, v, values):
+            check_values({**values})
+            return v
 
-    model = Model(foo='foo_value', bar='bar_value')
+    model = Model(foo='foo_value', pika='bar_value', baz='baz_value')
     check_values.reset_mock()
 
+    assert list(dict(model).items()) == [('foo', 'foo_value'), ('bar', 'bar_value'), ('baz', 'baz_value')]
+
     model.foo = 'new_foo_value'
-    check_values.assert_called_once_with({'bar': 'bar_value'})
+    check_values.assert_called_once_with({'bar': 'bar_value', 'baz': 'baz_value'})
+    check_values.reset_mock()
+
+    model.bar = 'new_bar_value'
+    check_values.assert_called_once_with({'foo': 'new_foo_value', 'baz': 'baz_value'})
+
+    # ensure field order is the same
+    assert list(dict(model).items()) == [('foo', 'new_foo_value'), ('bar', 'new_bar_value'), ('baz', 'baz_value')]
 
 
 def test_exceptions_in_field_validators_restore_original_field_value():