Repository: dynaconf/dynaconf

Issue:
[bug] Casting does not work with Box'd settings (eg dicts)
**Describe the bug**
Using any of the cast or "as_bool" etc methods do not work with nested/boxed settings.

**To Reproduce**

<details>
<summary> Python interpreter </summary>

```python

>>> settings.UPSTREAM_ROOK 
<Box: {'BUILD_ROOK_FROM_GIT': 'TRUE'}>
>>> settings.UPSTREAM_ROOK.BUILD_ROOK_FROM_GIT
'TRUE'
>>> settings.as_bool('UPSTREAM_ROOK.BUILD_ROOK_FROM_GIT')
False
>>> settings.get('UPSTREAM_ROOK.BUILD_ROOK_FROM_GIT') 
'TRUE'
>>> settings.get('UPSTREAM_ROOK.BUILD_ROOK_FROM_GIT', cast="@bool")
False

```
</details>

2. Having the following config files:

<!-- Please adjust if you are using different files and formats! -->

<details>
<summary> Config files </summary>

```
[upstream_rook]
build_rook_from_git = true
```


</details>


**Expected behavior**

`settings.as_bool('UPSTREAM_ROOK.BUILD_ROOK_FROM_GIT')` returns `True`.




Discussion:

Resolving patch:
diff --git a/dynaconf/base.py b/dynaconf/base.py
--- a/dynaconf/base.py
+++ b/dynaconf/base.py
@@ -351,7 +351,9 @@ def as_dict(self, env=None, internal=False):
 
     to_dict = as_dict  # backwards compatibility
 
-    def _dotted_get(self, dotted_key, default=None, parent=None, **kwargs):
+    def _dotted_get(
+        self, dotted_key, default=None, parent=None, cast=None, **kwargs
+    ):
         """
         Perform dotted key lookups and keep track of where we are.
         :param key: The name of the setting value, will always be upper case
@@ -364,11 +366,15 @@ def _dotted_get(self, dotted_key, default=None, parent=None, **kwargs):
 
         # If we've reached the end, or parent key not found, then return result
         if not keys or result == default:
+            if cast and cast in converters:
+                return get_converter(cast, result, box_settings=self)
+            elif cast is True:
+                return parse_conf_data(result, tomlfy=True, box_settings=self)
             return result
 
         # If we've still got key elements to traverse, let's do that.
         return self._dotted_get(
-            ".".join(keys), default=default, parent=result, **kwargs
+            ".".join(keys), default=default, parent=result, cast=cast, **kwargs
         )
 
     def get(
@@ -394,6 +400,10 @@ def get(
         :param parent: Is there a pre-loaded parent in a nested data?
         :return: The value if found, default or None
         """
+        nested_sep = self._store.get("NESTED_SEPARATOR_FOR_DYNACONF")
+        if nested_sep and nested_sep in key:
+            # turn FOO__bar__ZAZ in `FOO.bar.ZAZ`
+            key = key.replace(nested_sep, ".")
 
         if "." in key and dotted_lookup:
             return self._dotted_get(
@@ -458,7 +468,7 @@ def get_environ(self, key, default=None, cast=None):
         if data:
             if cast in converters:
                 data = get_converter(cast, data, box_settings=self)
-            if cast is True:
+            elif cast is True:
                 data = parse_conf_data(data, tomlfy=True, box_settings=self)
         return data
 
diff --git a/dynaconf/utils/__init__.py b/dynaconf/utils/__init__.py
--- a/dynaconf/utils/__init__.py
+++ b/dynaconf/utils/__init__.py
@@ -47,6 +47,8 @@ def object_merge(old, new, unique=False, full_path=None):
                 and key == full_path[-1]
                 and existing_value is value
             ):
+                # Here Be The Dragons
+                # This comparison needs to be smarter
                 continue
 
             if key not in new:
diff --git a/example/issues/445_casting/app.py b/example/issues/445_casting/app.py
new file mode 100644
--- /dev/null
+++ b/example/issues/445_casting/app.py
@@ -0,0 +1,11 @@
+from dynaconf import Dynaconf
+
+settings = Dynaconf(load_dotenv=True)
+
+assert settings.get("UPSTREAM_ROOK.BUILD_ROOK_FROM_GIT") == "TRUE"
+
+assert (
+    settings.as_bool("UPSTREAM_ROOK.BUILD_ROOK_FROM_GIT") is True
+), settings.as_bool("UPSTREAM_ROOK.BUILD_ROOK_FROM_GIT")
+
+assert settings.get("UPSTREAM_ROOK__BUILD_ROOK_FROM_GIT", cast="@bool") is True


Test patch: