Repository: samuelcolvin/pydantic (python)

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())
```



Pull request description:
fix: keep the order of the fields when `validate_assignment` is set
## Change Summary
Following #2000 and #2046 we can't `pop` the current value when assigning
a new one (which was probably the most efficient) as we want to keep the
order in the `__dict__`.
So let's do a shallow copy of the `__dict__` without the current value

## Related issue number
fix #2073

## Checklist

* [ ] Unit tests for the changes exist
* [ ] Tests pass on CI and coverage remains at 100%
* [ ] Documentation reflects the changes where applicable
* [ ] `changes/<pull request or issue id>-<github username>.md` file added describing change
  (see [changes/README.md](https://github.com/samuelcolvin/pydantic/blob/master/changes/README.md) for details)


Patch:
diff --git a/changes/2065-PrettyWood.md b/changes/2065-PrettyWood.md
new file mode 100644
index 000000000..8a5643f83
--- /dev/null
+++ b/changes/2065-PrettyWood.md
@@ -0,0 +1,1 @@
+fix: forward all the params of the stdlib `dataclass` when converted into _pydantic_ `dataclass`
\ No newline at end of file
diff --git a/changes/2073-PrettyWood.md b/changes/2073-PrettyWood.md
new file mode 100644
index 000000000..397fc78fe
--- /dev/null
+++ b/changes/2073-PrettyWood.md
@@ -0,0 +1,1 @@
+fix: keep the order of the fields when `validate_assignment` is set
\ No newline at end of file
diff --git a/docs/examples/dataclasses_stdlib_with_basemodel.py b/docs/examples/dataclasses_stdlib_with_basemodel.py
index 35ff1e883..92b494891 100644
--- a/docs/examples/dataclasses_stdlib_with_basemodel.py
+++ b/docs/examples/dataclasses_stdlib_with_basemodel.py
@@ -5,14 +5,20 @@ from typing import Optional
 from pydantic import BaseModel, ValidationError
 
 
+@dataclasses.dataclass(frozen=True)
+class User:
+    name: str
+
+
 @dataclasses.dataclass
 class File:
     filename: str
-    last_modification_time: Optional[datetime]
+    last_modification_time: Optional[datetime] = None
 
 
 class Foo(BaseModel):
     file: File
+    user: Optional[User] = None
 
 
 file = File(
@@ -25,3 +31,9 @@ try:
     Foo(file=file)
 except ValidationError as e:
     print(e)
+
+foo = Foo(file=File(filename='myfile'), user=User(name='pika'))
+try:
+    foo.user.name = 'bulbi'
+except dataclasses.FrozenInstanceError as e:
+    print(e)
diff --git a/docs/usage/dataclasses.md b/docs/usage/dataclasses.md
index 54dbcbbf3..536d91205 100644
--- a/docs/usage/dataclasses.md
+++ b/docs/usage/dataclasses.md
@@ -71,8 +71,9 @@ _(This script is complete, it should run "as is")_
 
 ### Use of stdlib dataclasses with `BaseModel`
 
-Bear in mind that stdlib dataclasses (nested or not) are **automatically converted** into _pydantic_ dataclasses
-when mixed with `BaseModel`!
+Bear in mind that stdlib dataclasses (nested or not) are **automatically converted** into _pydantic_
+dataclasses when mixed with `BaseModel`! Furthermore the generated _pydantic_ dataclass will have
+the **exact same configuration** (`order`, `frozen`, ...) as the original one.
 
 ```py
 {!.tmp_examples/dataclasses_stdlib_with_basemodel.py!}
diff --git a/docs/usage/settings.md b/docs/usage/settings.md
index a06ea52d4..9f9c50580 100644
--- a/docs/usage/settings.md
+++ b/docs/usage/settings.md
@@ -135,7 +135,7 @@ Placing secret values in files is a common pattern to provide sensitive configur
 A secret file follows the same principal as a dotenv file except it only contains a single value and the file name 
 is used as the key. A secret file will look like the following:
 
-`/var/run/db_password`:
+`/var/run/database_password`:
 ```
 super_secret_database_password
 ```
diff --git a/pydantic/dataclasses.py b/pydantic/dataclasses.py
index 28e2f9509..0d28e1b20 100644
--- a/pydantic/dataclasses.py
+++ b/pydantic/dataclasses.py
@@ -220,6 +220,9 @@ def make_dataclass_validator(_cls: Type[Any], config: Type['BaseConfig']) -> 'Ca
     """
     Create a pydantic.dataclass from a builtin dataclass to add type validation
     and yield the validators
+    It retrieves the parameters of the dataclass and forwards them to the newly created dataclass
     """
-    cls = dataclass(_cls, config=config)
+    dataclass_params = _cls.__dataclass_params__
+    stdlib_dataclass_parameters = {param: getattr(dataclass_params, param) for param in dataclass_params.__slots__}
+    cls = dataclass(_cls, config=config, **stdlib_dataclass_parameters)
     yield from _get_validators(cls)
diff --git a/pydantic/main.py b/pydantic/main.py
index 22a18dd10..3d57b1bbc 100644
--- a/pydantic/main.py
+++ b/pydantic/main.py
@@ -384,14 +384,14 @@ class BaseModel(Representation, metaclass=ModelMetaclass):
 
             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_dataclasses.py b/tests/test_dataclasses.py
index 061b73d09..e213ff567 100644
--- a/tests/test_dataclasses.py
+++ b/tests/test_dataclasses.py
@@ -777,3 +777,21 @@ def test_dataclass_arbitrary():
             arbitrary_types_allowed = True
 
     TestModel(a=ArbitraryType(), b=(ArbitraryType(), [ArbitraryType()]))
+
+
+def test_forward_stdlib_dataclass_params():
+    @dataclasses.dataclass(frozen=True)
+    class Item:
+        name: str
+
+    class Example(BaseModel):
+        item: Item
+        other: str
+
+        class Config:
+            arbitrary_types_allowed = True
+
+    e = Example(item=Item(name='pika'), other='bulbi')
+    e.other = 'bulbi2'
+    with pytest.raises(dataclasses.FrozenInstanceError):
+        e.item.name = 'pika2'
diff --git a/tests/test_validators.py b/tests/test_validators.py
index 9097c17d2..62a68b031 100644
--- a/tests/test_validators.py
+++ b/tests/test_validators.py
@@ -5,7 +5,7 @@ from typing import Dict, List, Optional, Tuple
 
 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 @@ def test_field_that_is_being_validated_is_excluded_from_validator_values(mocker)
         @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():