Repository: pydantic/pydantic
Pull request: Fix dict with extra keys
<!-- Thank you for your contribution! -->
<!-- See https://pydantic-docs.helpmanual.io/#contributing-to-pydantic for help on Contributing -->
<!-- Don't worry about making lots of commits on a pull request, they'll be squashed on merge anyway -->

## Change Summary
This will fix key error that occurs when calling `model.dict(by_alias=True)` with the model contains extra keys.

## Related issue number
Resolves #488

<!-- Are there any issues opened that will be resolved by merging this change? -->

## Checklist

* [x] Unit tests for the changes exist
* [x] Tests pass on CI and coverage remains at 100%
* [x] Documentation reflects the changes where applicable
* [x] `HISTORY.rst` has been updated
  * if this is the first change since a release, please add a new section
  * include the issue number or this pull request number `#<number>`
  * include your github username `@<whomever>`


Problem statement:
`.dict(by_alias=True)` throws an exception when trying to call with extra keys
<!-- Questions, Feature Requests, and Bug Reports are all welcome -->
<!-- delete as applicable: -->
# Bug

* OS: Mac OS
* Python version: 
```
3.7.2 (default, Mar 19 2019, 17:39:18) 
[Clang 10.0.0 (clang-1000.11.45.5)]
```
* Pydantic version: 0.23

I'm trying to use extra columns for my model. And I expected `.dict(by_alias=True)` to return dict for my model with alias, but I received an exception. I think this is because `get_key` is using `self.fields` that doesn't include extra keys.
```py
from pydantic import Schema, Extra, BaseModel, BaseConfig


class ModelTest(BaseModel):
    a: str = Schema(None, alias='b')

    class Config(BaseConfig):
        extra = Extra.allow


if __name__ == '__main__':
        m = ModelTest(extra_column='extra')
        print(m.extra_column)  # extra
        print(m.dict())  # {'a': None, 'extra_column': 'extra'}
        print(m.dict(by_alias=True))  # error
```

```
Traceback (most recent call last):
  File "...", line 41, in ...
    print(m.dict(by_alias=True))  # error
  File ".../python3.7/site-packages/pydantic/main.py", line 282, in dict
    get_key(k): v for k, v in self._iter(by_alias=by_alias, skip_defaults=skip_defaults)
  File ".../python3.7/site-packages/pydantic/main.py", line 282, in <dictcomp>
    get_key(k): v for k, v in self._iter(by_alias=by_alias, skip_defaults=skip_defaults)
  File ".../python3.7/site-packages/pydantic/main.py", line 293, in <lambda>
    return lambda fields, key: fields[key].alias
KeyError: 'extra_column'
```

Hints from issue discussion:
thanks for reporting, fix shouldn't be too hard. PR welcome or I'll fix in a week or so.

Commit message: Fix PR number

Patch to review:
diff --git a/HISTORY.rst b/HISTORY.rst
index dff3e4e5b6e..805c294a8b5 100644
--- a/HISTORY.rst
+++ b/HISTORY.rst
@@ -6,6 +6,7 @@ History
 v0.25 (unreleased)
 ..................
 * Improve documentation on self-referencing models and annotations, #487 by @theenglishway
+* fix ``.dict()`` with extra keys, #490 by @JaewonKim
 
 v0.24 (2019-04-23)
 ..................
diff --git a/pydantic/main.py b/pydantic/main.py
index dfa2b2184aa..6873842aa2d 100644
--- a/pydantic/main.py
+++ b/pydantic/main.py
@@ -289,7 +289,7 @@ def dict(
 
     def _get_key_factory(self, by_alias: bool) -> Callable[..., str]:
         if by_alias:
-            return lambda fields, key: fields[key].alias
+            return lambda fields, key: fields[key].alias if key in fields else key
 
         return lambda _, key: key
 
diff --git a/tests/test_main.py b/tests/test_main.py
index d157bdd33e0..527afae8b13 100644
--- a/tests/test_main.py
+++ b/tests/test_main.py
@@ -3,7 +3,7 @@
 
 import pytest
 
-from pydantic import BaseModel, Extra, NoneBytes, NoneStr, Required, ValidationError, constr
+from pydantic import BaseConfig, BaseModel, Extra, NoneBytes, NoneStr, Required, Schema, ValidationError, constr
 
 
 def test_success():
@@ -576,3 +576,15 @@ class MyModel(BaseModel):
     assert "json" in dir(m)
     assert "attribute_a" in dir(m)
     assert "attribute_b" in dir(m)
+
+
+def test_dict_with_extra_keys():
+    class MyModel(BaseModel):
+        a: str = Schema(None, alias='alias_a')
+
+        class Config(BaseConfig):
+            extra = Extra.allow
+
+    m = MyModel(extra_key='extra')
+    assert m.dict() == {'a': None, 'extra_key': 'extra'}
+    assert m.dict(by_alias=True) == {'alias_a': None, 'extra_key': 'extra'}


Code review comments:
diff_hunk: @@ -576,3 +576,15 @@ class MyModel(BaseModel):
     assert "json" in dir(m)
     assert "attribute_a" in dir(m)
     assert "attribute_b" in dir(m)
+
+
+def test_dict_with_extra_keys():
+    class MyModel(BaseModel):
+        a: str = Schema(None, alias='alias_a')
+
+        class Config(BaseConfig):
path: tests/test_main.py
text: @user1:
you don't need to inherit from `BaseConfig` here.

@author:
Thanks! @user1