Repository: pydantic/pydantic

Issue:
BaseModel.construct does not set aliased fields consistently with standard instantiation
### 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.8.2
            pydantic compiled: False
                 install path: /Users/kyleamos/Library/Caches/pypoetry/virtualenvs/api-KwVDv1SR-py3.9/lib/python3.9/site-packages/pydantic
               python version: 3.9.1 (default, Jan 18 2022, 12:54:24)  [Clang 13.0.0 (clang-1300.0.29.30)]
                     platform: macOS-12.1-arm64-arm-64bit
     optional deps. installed: ['email-validator', 'typing-extensions']
```
<!-- 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 pydantic import BaseConfig, BaseModel, Field


class Foo(BaseModel):
    class Config(BaseConfig):
        allow_population_by_field_name = True

    bar_: int = Field(..., alias="bar")


expected_value = 2
assert Foo(bar=expected_value).bar_ == expected_value
assert Foo.construct(bar=expected_value).bar_ == expected_value  # AttributeError: 'Foo' object has no attribute 'bar_' 
...
```



Discussion:

Resolving patch:
diff --git a/pydantic/main.py b/pydantic/main.py
--- a/pydantic/main.py
+++ b/pydantic/main.py
@@ -596,7 +596,9 @@ def construct(cls: Type['Model'], _fields_set: Optional['SetStr'] = None, **valu
         m = cls.__new__(cls)
         fields_values: Dict[str, Any] = {}
         for name, field in cls.__fields__.items():
-            if name in values:
+            if field.alt_alias and field.alias in values:
+                fields_values[name] = values[field.alias]
+            elif name in values:
                 fields_values[name] = values[name]
             elif not field.required:
                 fields_values[name] = field.get_default()


Test patch:
diff --git a/tests/test_aliases.py b/tests/test_aliases.py
--- a/tests/test_aliases.py
+++ b/tests/test_aliases.py
@@ -1,5 +1,6 @@
 import re
-from typing import Any, List, Optional
+from contextlib import nullcontext as does_not_raise
+from typing import Any, ContextManager, List, Optional
 
 import pytest
 
@@ -345,3 +346,39 @@ class Model(BaseModel):
     m = Model(**data)
     assert m.empty_string_key == 123
     assert m.dict(by_alias=True) == data
+
+
+@pytest.mark.parametrize(
+    'use_construct, allow_population_by_field_name_config, arg_name, expectation',
+    [
+        [False, True, 'bar', does_not_raise()],
+        [False, True, 'bar_', does_not_raise()],
+        [False, False, 'bar', does_not_raise()],
+        [False, False, 'bar_', pytest.raises(ValueError)],
+        [True, True, 'bar', does_not_raise()],
+        [True, True, 'bar_', does_not_raise()],
+        [True, False, 'bar', does_not_raise()],
+        [True, False, 'bar_', does_not_raise()],
+    ],
+)
+def test_allow_population_by_field_name_config(
+    use_construct: bool,
+    allow_population_by_field_name_config: bool,
+    arg_name: str,
+    expectation: ContextManager,
+):
+    expected_value: int = 7
+
+    class Foo(BaseModel):
+        bar_: int = Field(..., alias='bar')
+
+        class Config(BaseConfig):
+            allow_population_by_field_name = allow_population_by_field_name_config
+
+    with expectation:
+        if use_construct:
+            f = Foo.construct(**{arg_name: expected_value})
+        else:
+            f = Foo(**{arg_name: expected_value})
+
+        assert f.bar_ == expected_value