Repository: jazzband/django-debug-toolbar

Issue:
False positive for debug_toolbar.W006?
After upgrading to v3.2.3 I get the system warning W006, and an advisement to set _APP_DIRS=True_ for at least one of my template settings.

Shouldn't that check also be looking at the value of _loaders_ in order to avoid false warnings?

For example, the project in question had the following template settings:

```python
TEMPLATES = [{
    'BACKEND': 'django.template.backends.django.DjangoTemplates',
    'DIRS': [SITE_ROOT / 'templates'],
    'OPTIONS': {
        'builtins': [
            'django.templatetags.static',
            'common.templatetags.builtins',
        ],
        'context_processors': [
            'common.context_processors.constants',
            'django.template.context_processors.debug',
            'django.template.context_processors.request',
            'django.template.context_processors.media',
            'django.contrib.auth.context_processors.auth',
            'django.contrib.messages.context_processors.messages',
        ],
        'loaders': [
            'django.template.loaders.filesystem.Loader',
            'django.template.loaders.app_directories.Loader',
        ],
    },
}]
```
Django Debug Toolbar works wonderfully with these settings. My understanding is that the _app_directories_ loader is able to find the templates that the toolbar needs. The W0006 warning seems to be a false postive here (but to be honest, it's been a couple of years since I created this config).

As a quick experiment I tried just deleting the loaders option and adding the suggested APP_DIRS setting. It resulted in a key error for 'django' during deployment while trying to run migrate - so I've reverted back to v3.2.2 for now.



Discussion:
I was able to confirm that the example app can run with `APP_DIRS` set to False, but with the `django.template.loaders.app_directories.Loader` loader specified.

You can silence this check by making use of the [SILENCED_SYSTEM_CHECKS](https://docs.djangoproject.com/en/3.2/ref/settings/#std:setting-SILENCED_SYSTEM_CHECKS) setting.
We could make the check a bit smarter by inspecting `...["OPTIONS"]["loaders"]`.
I attempted to do this and discovered, that Django automatically adds the cached loader since 1.11 when `"debug"` is `False`:
https://github.com/django/django/commit/277fe2e8f2ee35cd389b079ce7691491bb5738ec

So, there's no good reason to specify `"loaders"` except when you really want to add a custom loader of your own. It's even worse than NOT specifying it, since you don't get the automatic advantage of caching used templates.

This only affected us since we have been cargo culting our settings for years and years now.

I think it's fine to require an addition to `SILENCED_SYSTEM_CHECKS` if you really want to specify your own loaders.

I have a new suggestion to make: We could add the hint that people may just want to drop `"loaders"` from their configuration and use `"APP_DIRS": True` (or `False`) instead 😄
Thanks for doing the research on that!

> I have a new suggestion to make: We could add the hint that people may just want to drop "loaders" from their configuration and use "APP_DIRS": True (or False) instead smile

It makes sense to include the information for devs to make a choice from. It's only a warning check and doesn't break anything so I'm in agreement that the requirement of needing to use `SILENCED_SYSTEM_CHECKS` is reasonable.
> This only affected us since we have been cargo culting our settings for years and years now.

You may be right about this, it looks like it was the cause of my problem too. I found the time to look into the error I was getting at deployment time and it was related to us manually wrapping the template loaders in the cached loader for production (obviously no longer needed). Deleting all template loader settings resolved both the debug_toolbar warning and the error. 

I apologise if _my_ issue was in fact the red herring! Thank you, everybody.
Don't worry, there definitely isn't a need to apologize. 
> I attempted to do this and discovered, that Django automatically adds the cached loader since 1.11 when "debug" is False:
django/django@277fe2e

@matthiask it looks like that loader isn't included when debug = True, so it shouldn't matter for the toolbar.

Resolving patch:
diff --git a/debug_toolbar/apps.py b/debug_toolbar/apps.py
--- a/debug_toolbar/apps.py
+++ b/debug_toolbar/apps.py
@@ -22,6 +22,24 @@ def ready(self):
         DebugToolbar.get_panel_classes()
 
 
+def check_template_config(config):
+    """
+    Checks if a template configuration is valid.
+
+    The toolbar requires either the toolbars to be unspecified or
+    ``django.template.loaders.app_directories.Loader`` to be
+    included in the loaders.
+    If custom loaders are specified, then APP_DIRS must be True.
+    """
+    app_dirs = config.get("APP_DIRS", False)
+    loaders = config.get("OPTIONS", {}).get("loaders", None)
+    # By default the app loader is included.
+    has_app_loaders = (
+        loaders is None or "django.template.loaders.app_directories.Loader" in loaders
+    )
+    return has_app_loaders or app_dirs
+
+
 @register
 def check_middleware(app_configs, **kwargs):
     from debug_toolbar.middleware import DebugToolbarMiddleware
@@ -30,13 +48,16 @@ def check_middleware(app_configs, **kwargs):
     gzip_index = None
     debug_toolbar_indexes = []
 
-    if all(not config.get("APP_DIRS", False) for config in settings.TEMPLATES):
+    if all(not check_template_config(config) for config in settings.TEMPLATES):
         errors.append(
             Warning(
                 "At least one DjangoTemplates TEMPLATES configuration needs "
-                "to have APP_DIRS set to True.",
+                "to use django.template.loaders.app_directories.Loader or "
+                "have APP_DIRS set to True.",
                 hint=(
-                    "Use APP_DIRS=True for at least one "
+                    "Include django.template.loaders.app_directories.Loader "
+                    'in ["OPTIONS"]["loaders"]. Alternatively use '
+                    "APP_DIRS=True for at least one "
                     "django.template.backends.django.DjangoTemplates "
                     "backend configuration."
                 ),


Test patch:
diff --git a/tests/test_checks.py b/tests/test_checks.py
--- a/tests/test_checks.py
+++ b/tests/test_checks.py
@@ -134,21 +134,27 @@ def test_panels_is_empty(self):
                         "django.template.context_processors.request",
                         "django.contrib.auth.context_processors.auth",
                         "django.contrib.messages.context_processors.messages",
-                    ]
+                    ],
+                    "loaders": [
+                        "django.template.loaders.filesystem.Loader",
+                    ],
                 },
             },
         ]
     )
-    def test_templates_is_using_app_dirs_false(self):
+    def test_check_w006_invalid(self):
         errors = run_checks()
         self.assertEqual(
             errors,
             [
                 Warning(
-                    "At least one DjangoTemplates TEMPLATES configuration "
-                    "needs to have APP_DIRS set to True.",
+                    "At least one DjangoTemplates TEMPLATES configuration needs "
+                    "to use django.template.loaders.app_directories.Loader or "
+                    "have APP_DIRS set to True.",
                     hint=(
-                        "Use APP_DIRS=True for at least one "
+                        "Include django.template.loaders.app_directories.Loader "
+                        'in ["OPTIONS"]["loaders"]. Alternatively use '
+                        "APP_DIRS=True for at least one "
                         "django.template.backends.django.DjangoTemplates "
                         "backend configuration."
                     ),
@@ -156,3 +162,39 @@ def test_templates_is_using_app_dirs_false(self):
                 )
             ],
         )
+
+    @override_settings(
+        TEMPLATES=[
+            {
+                "NAME": "use_loaders",
+                "BACKEND": "django.template.backends.django.DjangoTemplates",
+                "APP_DIRS": False,
+                "OPTIONS": {
+                    "context_processors": [
+                        "django.template.context_processors.debug",
+                        "django.template.context_processors.request",
+                        "django.contrib.auth.context_processors.auth",
+                        "django.contrib.messages.context_processors.messages",
+                    ],
+                    "loaders": [
+                        "django.template.loaders.app_directories.Loader",
+                    ],
+                },
+            },
+            {
+                "NAME": "use_app_dirs",
+                "BACKEND": "django.template.backends.django.DjangoTemplates",
+                "APP_DIRS": True,
+                "OPTIONS": {
+                    "context_processors": [
+                        "django.template.context_processors.debug",
+                        "django.template.context_processors.request",
+                        "django.contrib.auth.context_processors.auth",
+                        "django.contrib.messages.context_processors.messages",
+                    ],
+                },
+            },
+        ]
+    )
+    def test_check_w006_valid(self):
+        self.assertEqual(run_checks(), [])