Summary
In a Django project with APP_DIRS=True, the template engine searches for included templates relative to the app folder as well as TEMPLATES['DIRS']. Using a relative path like "../../users/detail/footer" in {% include %} skips the app root, causing Django to look in a non‑existent location and raise TemplateDoesNotExist.
Root Cause
APP_DIRS=Truetells Django to load templates fromapp/templates/automatically.- The
{% include "..." %}tag resolves paths relative to the current template’s location and to each app’s template directory. - The relative path
"../../users/detail/footer"tries to go two levels up fromtemplates/partials/listings/detail/, which is outside thetemplatesroot, so Django cannot find it. - Django does not support jumping out of the
templatesdirectory with../../.
Why This Happens in Real Systems
- Deployments may have multiple
templatesdirectories (project‑wide, per‑app). - Relative path calculation varies between development and production due to different working directories.
- OPAC of
"APP_DIRS=True"automatically flattens app templates, so a relative path often leads to an unintended search location.
Real-World Impact
- Users see a 500 Internal Server Error when a page references the missing partial.
- Development velocity drops because the error appears only at runtime, not during code review.
- Hidden template typos or duplicate names can remain undetected until the faulty path is executed.
Example or Code (if necessary and relevant)
{% include "partials/users/detail/footer.html" %}
or, if the file is in the root templates directory:
{% include "footer.html" %}
Use an absolute path (starting at the project root) instead of a relative jump.
How Senior Engineers Fix It
-
Use absolute template names:
{% include "partials/users/detail/footer.html" %}{% include "users/detail/footer.html" %}
-
Verify
TEMPLATES['DIRS']includes the correct base folder and avoid mixing relative and absolute paths. -
Add a unit test that renders “listings/detail/user_card.html” and checks that the footer is loaded.
from django.test import SimpleTestCase, override_settings from django.template import Template, Context
class TemplateLoadingTest(SimpleTestCase):
def test_footer_includes(self):
tpl = Template(‘{% include “partials/users/detail/footer.html” %}’)
rendered = tpl.render(Context({}))
self.assertIn(‘Footer content’, rendered)
- Refactor folder structure to keep the same prefix for all templates:
`templates/partials/...` and `templates/users/...`.
---
## Why Juniors Miss It
- They assume Django interprets `"../../"` correctly, ignoring the app‑directory sandbox.
- They overlook that `APP_DIRS=True` already adds the whole `app/templates` tree to the search path.
- They may not notice that the `static` tag produces a path double‑quoted unnecessarily (e.g., `{% static '/images/...`), but it rarely causes the template error.
- Lack of **debug verbosity**: `TemplateDoesNotExist` is often reported without mentioning search directories, so the mis‑sized relative path goes unnoticed.
---
**Key takeaway:**
Always reference included templates with **absolute paths** relative to the project’s `templates` root; avoid crafting paths that step outside the template tree.