Repository: dynaconf/dynaconf

Issue:
Validation of Nested Settings with Titlecase Name Fails
**Describe the bug**

Validation of nested setting items that do not use uppercase letters does not work

**To Reproduce**

Steps to reproduce the behavior:

1. Having the following folder structure

   <!-- Describe or use the command `$ tree -v` and paste below -->
   
   <details>
   <summary> Project structure </summary>
   
   ```
   .
   ├── config.yaml
   └── test.py
   
   0 directories, 2 files
   ```
   </details>
   
2. Having the following config file:

   <!-- Please adjust if you are using different files and formats! -->
   
   <details>
   <summary> Config file </summary>
   
   **config.yaml**
   ```yaml
   DATABASE:
     HOST: localhost
   
   Operator:
     Name: Jane Doe
   ```
   
   </details>

3. Having the following app code:

   <details>
   <summary> Code </summary>
   
   **test.py**
   ```python
   from dynaconf import Dynaconf, Validator
   from pathlib import Path
   
   settings = Dynaconf(
       settings_file=[Path(__file__).parent.joinpath('config.yaml')])
   
   print(f"Host: {settings.DATABASE.HOST}")
   settings.validators.register(Validator('DATABASE.HOST', must_exist=True))
   settings.validators.validate()
   
   print(f"Name: {settings.Operator.Name}")
   settings.validators.register(Validator('Operator.Name', must_exist=True))
   settings.validators.validate()
   ```
   
   </details>

4. Executing under the following environment

   <details>
   <summary> Execution </summary>
   
   ```fish
   python3 test.py
   ```
   
   </details>

**Expected behavior**

The command should produce the following output:

```
Host: localhost
Name: Jane Doe
```

and exit with a zero exit code. 

**Actual behavior**

The last validation command fails:

```
Host: localhost
Name: Jane Doe
Traceback (most recent call last):
  File "/Users/rene/Downloads/Validation/test.py", line 13, in <module>
    settings.validators.validate()
  File "/Users/rene/.pyenv/versions/3.9.0/lib/python3.9/site-packages/dynaconf/validator.py", line 318, in validate
    validator.validate(self.settings)
  File "/Users/rene/.pyenv/versions/3.9.0/lib/python3.9/site-packages/dynaconf/validator.py", line 172, in validate
    self._validate_items(settings, settings.current_env)
  File "/Users/rene/.pyenv/versions/3.9.0/lib/python3.9/site-packages/dynaconf/validator.py", line 194, in _validate_items
    raise ValidationError(
dynaconf.validator.ValidationError: Operator.Name is required in env main
```

with exit code `1`. If I only use uppercase letters for the operator name configuration value:

```yaml
OPERATOR:
  NAME: Jane Doe
```

everything works as expected.

**Environment:**

 - OS: macOS `11.0.1`
 - Dynaconf Version: `3.1.2`


Discussion:

Resolving patch:
diff --git a/dynaconf/utils/boxing.py b/dynaconf/utils/boxing.py
--- a/dynaconf/utils/boxing.py
+++ b/dynaconf/utils/boxing.py
@@ -3,6 +3,7 @@
 
 from dynaconf.utils import recursively_evaluate_lazy_format
 from dynaconf.utils import upperfy
+from dynaconf.utils.functional import empty
 from dynaconf.vendor.box import Box
 
 
@@ -57,11 +58,22 @@ def copy(self):
             box_settings=self._box_config.get("box_settings"),
         )
 
+    def _case_insensitive_get(self, item, default=None):
+        """adds a bit of overhead but allows case insensitive get
+        See issue: #486
+        """
+        lower_self = {k.casefold(): v for k, v in self.items()}
+        return lower_self.get(item.casefold(), default)
+
     @evaluate_lazy_format
     def get(self, item, default=None, *args, **kwargs):
         if item not in self:  # toggle case
             item = item.lower() if item.isupper() else upperfy(item)
-        return super(DynaBox, self).get(item, default, *args, **kwargs)
+        value = super(DynaBox, self).get(item, empty, *args, **kwargs)
+        if value is empty:
+            # see Issue: #486
+            return self._case_insensitive_get(item, default)
+        return value
 
     def __dir__(self):
         keys = list(self.keys())
diff --git a/example/issues/486_title_case_validation/app.py b/example/issues/486_title_case_validation/app.py
new file mode 100644
--- /dev/null
+++ b/example/issues/486_title_case_validation/app.py
@@ -0,0 +1,16 @@
+from pathlib import Path
+
+from dynaconf import Dynaconf
+from dynaconf import Validator
+
+settings = Dynaconf(
+    settings_file=[Path(__file__).parent.joinpath("settings.yaml")]
+)
+
+print(f"Host: {settings.DATABASE.HOST}")
+settings.validators.register(Validator("DATABASE.HOST", must_exist=True))
+settings.validators.validate()
+
+print(f"Name: {settings.Operator.Name}")
+settings.validators.register(Validator("Operator.Name", must_exist=True))
+settings.validators.validate()


Test patch: