This is a long answer. The idea is to really explain how Django works when loading a template. I even show you some Django source code to explain some points.
Django uses engines to load the templates. This answers works for the default engine DjangoTemplates
(django.template.backends.django.DjangoTemplates
)
Let's review your comment:
"The django template folder requires creating a subfolder with the
name of the app which then contains the template files."
No, Django doesn't require you to create a subfolder with the name of the app inside the templates folder. IT IS NOT A REQUIREMENT, it is just a recommendation.
But why? Let's see it in steps.
1) Where does Django look for template files?
Django searches for template directories in a number of places,
depending on your template loading settings.
There are two places that are defined in the settings.py
file. When configuring TEMPLATES
you have DIRS
and APP_DIRS
. Like this:
javascript
TEMPLATES = [{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [BASE_DIR/"templates"],
'APP_DIRS': True,
...
}]
* `DIRS`:
> Is a list of directories where Django looks for template source
> files...
>
> This should be set to a list of strings that contain full
> paths to your template directories...
>
> Your templates can go anywhere you want, as long as the directories and templates are readable by the web server...
Example:
TEMPLATES = [{
'DIRS': [
'/home/html/templates/lawrence.com',
'/home/html/templates/default',
],},]
**So you have no required structure from Django**. You can have as many directories you want, where you want, as long as the paths are listed here and readable by the web server. A common case is when you want to have a project folder for your templates that all apps are going to use:
`'DIRS': [BASE_DIR/"templates"]`
This tells Django to find the files in the `templates` folder located in the base directory (the root level).
* `APP_DIRS`:
> Tells whether the engine should look for templates inside installed
> applications...
>
> When `APP_DIRS` is `True`, DjangoTemplates engines look for templates in the templates subdirectory of installed applications...
>
> By convention DjangoTemplates looks for a “templates” subdirectory in
> each of the INSTALLED_APPS...
>
> For each app in INSTALLED_APPS, the loader looks for a templates
> subdirectory. If the directory exists, Django looks for templates in
> there...
It's a boolean value, `True` or `False`.
If `True`, it will look for a `templates` subdirectory inside each app:
project/
appname/
templates/
**This is the only structure that Django requires from you.** If you want to have templates in each app, you must have a `templates` subfolder in the app directory.
If you have this apps:
INSTALLED_APPS = ['myproject.polls', 'myproject.music']
And `APP_DIRS: True` Django will look fot templates here.
/path/to/myproject/polls/templates/
/path/to/myproject/music/templates/
## 2) Why do people recommend having another folder with the app name inside the templates folder (in installed apps)?
It's a matter of orginizing your code and helping Django find the file that you really requested (template namespacing).
Something important to remember: when the template engine loads templates, it checks the template directories in the order they are defined in the `DIRS` setting. If a template with the same name is found in multiple directories, the first one found is used.
After checking the `DIRS` directories, the template engine looks for templates in the `APP_DIRS` directories. If a template with the same name is found in multiple application directories, the one in the first application listed in INSTALLED_APPS is used.
Let's see what could happen if we don't do template namespacing. We have this folders:
INSTALLED_APPS = ['project.app_1', 'project.app_2']
project/
app_1/
templates/
detail.html
app_2/
templates/
detail.html
If I'm working in the `app_2` view and want to load the template `detail.html` I would have a surprise. Instead of loading the template from `app_2`I would load the template from `app_1`. This is because the files have the same name and `app_1` comes first in `INSTALLED_APPS`.
To avoid this problem we add the app's name inside the template folder.
project/
app_1/
templates/
app_1/
detail.html
app_2/
templates/
app_2/
detail.html
To load the template from the view I would need "app_2/detail.html". I'm being much more specific.
## 3) You can check this in the Django source code.
Go to django/django/template/loaders/app_directories.py and you'll find:
class Loader(FilesystemLoader):
def get_dirs(self):
return get_app_template_dirs("templates")
Which calls `get_app_template_dirs()` and passes "template" as argument.
django/django/template/utils.py
@functools.lru_cache
def get_app_template_dirs(dirname):
"""
Return an iterable of paths of directories to load app templates from.
dirname is the name of the subdirectory containing templates inside installed applications.
[NOTE: Remember that "templates" was passed as argument, the dirname]
"""
template_dirs = [
Path(app_config.path) / dirname
for app_config in apps.get_app_configs()
if app_config.path and (Path(app_config.path) / dirname).is_dir()
]
# Immutable return value because it will be cached and shared by callers.
return tuple(template_dirs)
With `Path(app_config.path)/dirname` you get `appname/templates/`.
For each installed app found here `for app_config in apps.get_app_configs()`.
If the dirs exists `if app_config.path and (Path(app_config.path) / dirname).is_dir()`
`template_dirs` are all the dirs in the installed apps that have a template foler.
## 4) If you're working with Class Based Views (CBV) you can get some functionality done for you.
You have this list view for the `Post` model in the `Blog` app.
class BlogListView(ListView):
model = Post
In the absence of an explicit template name, Django will infer one from the object’s (model's) name. In this example:
blog/post_list.html
The structure is:
appname: Blog
model: Post
View type: List
appname/<model_name>_<view_type>.html
And as you already know, Django looks for a “templates” subdirectory in each of the INSTALLED_APPS.
So, if `APP_DIRS: True` the full path that the Class Based View would expect to load the templates is:
/path/to/project/blog/templates/blog/post_list.html
This is a predefined requirement of the CBV, but it can be modified if
you define a `template_name` argument.
class BlogListView(ListView):
model = Post
template_name = "blog/the_super_list_of_posts.html"
You can check this in the source code:
Go to django/django/views/generic/list.py
class MultipleObjectTemplateResponseMixin(TemplateResponseMixin):
"""Mixin for responding with a template and list of objects."""
template_name_suffix = "_list"
def get_template_names(self):
"""
Return a list of template names to be used for the request. Must return
a list. May not be called if render_to_response is overridden.
"""
try:
names = super().get_template_names()
except ImproperlyConfigured:
# If template_name isn't specified, it's not a problem --
# we just start with an empty list.
names = []
# If the list is a queryset, we'll invent a template name based on the
# app and model name. This name gets put at the end of the template
# name list so that user-supplied names override the automatically-
# generated ones.
if hasattr(self.object_list, "model"):
opts = self.object_list.model._meta
names.append(
"%s/%s%s.html"
% (opts.app_label, opts.model_name,
self.template_name_suffix)
)
elif not names:
raise ImproperlyConfigured(
"%(cls)s requires either a 'template_name' attribute "
"or a get_queryset() method that returns a QuerySet."
% {
"cls": self.__class__.__name__,
}
)
return names
You have:
names = super().get_template_names() # get's the appname/templates
...
names.append(
"%s/%s%s.html"
% (opts.app_label, opts.model_name, self.template_name_suffix)
)
Where:
opts.app_label: is the app's name
opts.model_name: is the model's name
self.template_name_suffix: is the suffix, in this case "_list"
All together they make the default's template name that the CBV looks for:
app_label/templates/app_label/<model_name>_<template_name_suffix>.html
NOTE: Originally posted in StackOverflow
https://stackoverflow.com/a/75861673/6710903
**DOCUMENTATION.**
1. https://docs.djangoproject.com/en/4.1/ref/templates/api/
2. https://docs.djangoproject.com/en/4.1/topics/templates/
3. https://docs.djangoproject.com/en/4.2/intro/tutorial03/#write-views-that-actually-do-something
4. https://github.com/django/django/blob/main/django/template/loaders/app_directories.py
5. https://github.com/django/django/blob/main/django/template/utils.py
6. https://docs.djangoproject.com/en/4.1/topics/class-based-views/generic-display/
Top comments (0)