Skip to content

Instantly share code, notes, and snippets.

@jacklinke
Created October 22, 2024 19:35
Show Gist options
  • Save jacklinke/a98c121c50744f15b329316e3fea0106 to your computer and use it in GitHub Desktop.
Save jacklinke/a98c121c50744f15b329316e3fea0106 to your computer and use it in GitHub Desktop.
A management command to list all templates in the project.

A management command to list all templates in the project.

This command will list all templates in the project, depending on the template engines and loaders that are configured in the Django settings, and display them in the way you should add them to a {% url "" %} template tag or for rendering in a view.

By default, it will scan all directories in the TEMPLATES setting for template files.

To customize which template extensions that are scanned, set the SHOWTEMPLATE_EXTENSIONS setting in your Django settings file. The default is ['.html', '.htm', '.django', '.jinja', '.md']

Make sure to add this command to the management commands of an app in your project. For example, you can create a new app called 'example_app' and add this command to it.

Example Directory Structure:

└── ./
    └── example_project
        └── example_app
            └── management
                ├── commands
                │   ├── __init__.py
                │   └── showtemplates.py
                └── __init__.py

Call it with:

python manage.py showtemplates
"""A management command to list all templates in the project.
This command will list all templates in the project, depending on the template
engines and loaders that are configured in the Django settings, and display them
in the way you should add them to a `{% url "" %}` template tag or for rendering in
a view.
By default, it will scan all directories in the TEMPLATES setting for
template files.
To customize which template extensions that are scanned, set the
SHOWTEMPLATE_EXTENSIONS setting in your Django settings file. The default
is ['.html', '.htm', '.django', '.jinja', '.md']
Make sure to add this command to the management commands of an app in your
project. For example, you can create a new app called 'example_app' and add this
command to it.
Example Directory Structure:
└── ./
└── example_project
└── example_app
└── management
├── commands
│ ├── __init__.py
│ └── showtemplates.py
└── __init__.py
Call it with:
python manage.py showtemplates
"""
import os
from django.conf import settings
from django.core.management.base import BaseCommand
from django.template import engines
from django.template.loaders.app_directories import Loader as AppDirLoader
from django.template.loaders.filesystem import Loader as FilesystemLoader
class Command(BaseCommand):
"""A management command to list all templates in the project."""
help = "List all templates with their Django-relative paths"
def log(self, msg, level=1):
"""Log message if verbosity is high enough."""
if self.verbosity >= level:
self.stdout.write(msg)
def get_template_files(self, directory):
"""Get all template files in a directory.
Args:
directory (str): Directory to scan
Returns:
list: List of template file paths relative to the directory
"""
templates = []
if not os.path.exists(directory):
self.log(f"Directory does not exist: {directory}", 2)
return templates
self.log(f"Scanning directory: {directory}", 2)
for root, _, files in os.walk(directory):
for file in files:
template_extensions = getattr(
settings, "SHOWTEMPLATE_EXTENSIONS", [".html", ".htm", ".django", ".jinja", ".md"]
)
if file.endswith(tuple(template_extensions)):
full_path = os.path.join(root, file)
rel_path = os.path.relpath(full_path, directory)
templates.append(rel_path)
self.log(f"Found template: {rel_path}", 3)
return templates
def get_loader_templates(self, loader):
"""Get templates from a specific loader.
Args:
loader: Template loader instance
Returns:
set: Set of template paths
"""
templates = set()
loader_name = loader.__class__.__name__
self.log(f"Processing loader: {loader_name}", 2)
# Handle cached loader
if hasattr(loader, "loaders"):
self.log("Found cached loader, processing inner loaders", 2)
for inner_loader in loader.loaders:
templates.update(self.get_loader_templates(inner_loader))
return templates
# Get directories from filesystem loader
if isinstance(loader, FilesystemLoader):
self.log("Processing filesystem loader", 2)
dirs = loader.get_dirs()
self.log(f"Filesystem loader dirs: {dirs}", 2)
for template_dir in dirs:
templates.update(self.get_template_files(template_dir))
# Get directories from app directories loader
if isinstance(loader, AppDirLoader):
self.log("Processing app directories loader", 2)
app_template_dirs = loader.get_dirs()
self.log(f"App template dirs: {app_template_dirs}", 2)
for template_dir in app_template_dirs:
templates.update(self.get_template_files(template_dir))
return templates
def handle(self, *args, **options):
self.verbosity = options.get("verbosity", 1)
is_verbose = options.get("verbose", False)
if is_verbose:
self.verbosity = 3
all_templates = set()
# Process each template configuration
for template_config in settings.TEMPLATES:
self.stdout.write(f"\nProcessing template backend: {template_config['BACKEND']}")
# Find the engine that matches the BACKEND
backend = None
for engine in engines.all():
if engine.__class__.__name__ == template_config["BACKEND"].split(".")[-1]:
backend = engine
break
if backend is None:
self.log(f"Engine not found for backend {template_config['BACKEND']}", 2)
continue
engine = backend.engine # The actual Engine instance
# Process loaders
loaders = []
options_dict = template_config.get("OPTIONS", {})
loader_list = options_dict.get("loaders", [])
if loader_list:
self.log("Found loaders in OPTIONS:", 2)
loaders = engine.get_template_loaders(loader_list)
else:
self.log("No loaders found in OPTIONS, using engine's loaders", 2)
loaders = engine.template_loaders
# Process loaders
for loader in loaders:
templates = self.get_loader_templates(loader)
all_templates.update(templates)
# Sort results
template_list = sorted(all_templates)
if not template_list:
self.stdout.write("\nNo templates found.")
return
self.stdout.write("\nFound templates:")
self.stdout.write("\n".join(template_list))
self.stdout.write(f"\nTotal templates found: {len(template_list)}")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment