Repository: dynaconf/dynaconf

Issue:
[bug] FlaskDynaconf breaks default getter of flask config
**Describe the bug**
In pure Flask config if you query config for nonexisting key you get `KeyError` exception.
If use FlaskDynaconf if you query config for nonexisting key you get value `None`.

Such differences break thirdparty libraries, which suppose that flask config will raise `KeyError` when queriing nonexisting key (for example, `python-social-auth`: https://github.com/python-social-auth/social-core/blob/master/social_core/strategy.py#L45 which goes to https://github.com/python-social-auth/social-app-flask/blob/master/social_flask/strategy.py#L19)

**To Reproduce**
Correct behavior:

```
from flask import Flask

app = Flask(__name__)
print("NON_EXISTING" in app.config)
print(app.config["NON_EXISTING"])
```

will give you

```
False
Traceback (most recent call last):
  File "dc_poc/1_no_dynaconf.py", line 6, in <module>
    print(app.config["NON_EXISTING"])
KeyError: 'NON_EXISTING'
```

Incorrect behavior:

```
from flask import Flask
from dynaconf import FlaskDynaconf

app = Flask(__name__)
FlaskDynaconf(app=app)
print("NON_EXISTING" in app.config)
print(app.config["NON_EXISTING"])
```

will give you

```
False
None
```

1. Having the following folder structure

<!-- Describe or use the command `$ tree -v` and paste below -->

<details>
<summary> Project structure </summary>

```13:57 $ tree -v
.
├── 1_no_dynaconf.py
└── 2_dynaconf.py

0 directories, 2 files
```
</details>

2. Having the following config files:

<!-- Please adjust if you are using different files and formats! -->

<details>
<summary> Config files </summary>
No config files used at all
</details>

3. Having the following app code:

<details>
<summary> Code </summary>

**1_no_dynaconf.py**
```python
from flask import Flask

app = Flask(__name__)
print("NON_EXISTING" in app.config)
print(app.config["NON_EXISTING"])
```

**2_dynaconf.py**
```python
from flask import Flask
from dynaconf import FlaskDynaconf

app = Flask(__name__)
FlaskDynaconf(app=app)
print("NON_EXISTING" in app.config)
print(app.config["NON_EXISTING"])
```
</details>

4. Executing under the following environment

<details>
<summary> Execution </summary>

used Python 3.8.5, Flask 1.1.2, dynaconf 3.1.2

```bash
# other commands and details?
# virtualenv activation?

$ python 1_no_dynaconf.py
$ python 2_dynaconf.py
```

</details>

**Expected behavior**
Queriing non existing key via `config[key]` should raise `KeyError`, not returning `None`.

**Environment (please complete the following information):**
 - OS: Linux/Kubuntu20.04
 - Dynaconf Version 3.1.2
 - Flask 1.1.2

**Additional context**
Add any other context about the problem here.



Discussion:
Duplicate of #501 already merged in #502 and coming in the next release
> Duplicate of #501 already merged in #502 and coming in the next release

Not quite duplicate. #501 and #502 are about `in` syntax, which is `__contains__` for. But my case is accessing `variable[key]`, so `DynaconfConfig.__getitem__` is used, which calls `DynaconfConfig.get` which calls `flask.config.Config.get` with default value set to `None`. But accessing real `flask.config.Config` via `[]` does **not** uses default value.

Resolving patch:
diff --git a/dynaconf/contrib/flask_dynaconf.py b/dynaconf/contrib/flask_dynaconf.py
--- a/dynaconf/contrib/flask_dynaconf.py
+++ b/dynaconf/contrib/flask_dynaconf.py
@@ -151,14 +151,11 @@ def __init__(self, _settings, _app, *args, **kwargs):
         self._settings = _settings
         self._app = _app
 
-    def __contains__(self, item):
-        return hasattr(self, item)
-
     def __getitem__(self, key):
-        """
-        Flask templates always expects a None when key is not found in config
-        """
-        return self.get(key)
+        try:
+            return self._settings[key]
+        except KeyError:
+            return Config.__getitem__(self, key)
 
     def __setitem__(self, key, value):
         """
@@ -168,7 +165,7 @@ def __setitem__(self, key, value):
 
     def __getattr__(self, name):
         """
-        First try to get value from dynaconf then from Flask
+        First try to get value from dynaconf then from Flask Config
         """
         try:
             return getattr(self._settings, name)
diff --git a/example/flask_with_dotenv/app.py b/example/flask_with_dotenv/app.py
--- a/example/flask_with_dotenv/app.py
+++ b/example/flask_with_dotenv/app.py
@@ -20,7 +20,9 @@ def test():
     return app.config["HELLO"]
 
 
-app.add_url_rule(app.config.TEST_RULE, view_func=lambda: "test")
+app.add_url_rule(
+    app.config.get("TEST_RULE", "/default_route"), view_func=lambda: "test"
+)
 
 
 if __name__ == "__main__":
diff --git a/example/issues/194_flask_config/app.py b/example/issues/194_flask_config/app.py
--- a/example/issues/194_flask_config/app.py
+++ b/example/issues/194_flask_config/app.py
@@ -15,7 +15,7 @@
 
 app.config.set("MESSAGE", "bye")
 
-assert app.config["MESSAGE"] == "bye"
+assert app.config["MESSAGE"] == "bye", app.config["MESSAGE"]
 
 
 app.config["MESSAGE"] = "bazinga"


Test patch:
diff --git a/tests/test_base.py b/tests/test_base.py
--- a/tests/test_base.py
+++ b/tests/test_base.py
@@ -548,6 +548,18 @@ def test_dotted_traversal_access(settings):
     # First key is always transformed to upper()
     assert settings("nested_1.nested_2.nested_3.nested_4") is True
 
+    # using cast
+    settings.set("me.name", '@json ["bruno", "rocha"]')
+    settings.set("me.number", "42")
+    assert settings.get("me.name", cast=True, default=["bruno", "rocha"]) == [
+        "bruno",
+        "rocha",
+    ]
+    assert settings.get("me.number", cast="@int", default=42) == 42
+
+    # nested separator test
+    assert settings.get("ME__NUMBER") == "42"
+
 
 def test_dotted_set(settings):
     settings.set("MERGE_ENABLED_FOR_DYNACONF", False)
diff --git a/tests/test_flask.py b/tests/test_flask.py
--- a/tests/test_flask.py
+++ b/tests/test_flask.py
@@ -1,3 +1,4 @@
+import pytest
 from flask import Flask
 
 from dynaconf.contrib import FlaskDynaconf
@@ -69,6 +70,13 @@ def test_flask_dynaconf(settings):
     assert "HOSTNAME" in app.config
     assert "MY_VAR" in app.config
 
+    # ref: #521
+    assert "NONEXISTENETVAR" not in app.config
+    assert ("NONEXISTENETVAR" in app.config) is False
+
+    with pytest.raises(KeyError):
+        app.config["NONEXISTENETVAR"]
+
 
 def test_flask_with_dot_env():
     envvars = {