Created
November 11, 2024 20:38
-
-
Save jefftriplett/cdbbd05d435f102e9ecd4b7cb9e74f32 to your computer and use it in GitHub Desktop.
Django management command to flatten and copy templates into your projects templates folder
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from difflib import unified_diff | |
from pathlib import Path | |
import filecmp | |
import shutil | |
import djclick as click | |
from django.apps import apps | |
from django.conf import settings | |
from djclick import pass_verbosity | |
from rich import print | |
@click.command(help='Copy templates from all installed apps to the project template directory') | |
@click.option( | |
'--destination', | |
'-d', | |
default='templates', | |
help='Destination directory for templates (relative to project root)', | |
) | |
@click.option( | |
'--overwrite/--no-overwrite', | |
default=False, | |
help='Overwrite existing templates in destination', | |
) | |
@click.option( | |
'--dry-run', | |
is_flag=True, | |
help='Show what would be copied without making changes', | |
) | |
@click.option( | |
'--show-diff', | |
is_flag=True, | |
help='Show diff when templates have different content', | |
) | |
@pass_verbosity | |
def handle(destination, overwrite, dry_run, show_diff, verbosity): | |
project_root = Path(settings.BASE_DIR) | |
dest_dir = project_root / destination | |
if not dry_run: | |
dest_dir.mkdir(parents=True, exist_ok=True) | |
templates_copied = 0 | |
templates_skipped = 0 | |
templates_identical = 0 | |
templates_different = 0 | |
def compare_templates(src_path, dst_path): | |
"""Compare the contents of two template files.""" | |
if not dst_path.exists(): | |
return False, None | |
# First do a quick file comparison | |
if filecmp.cmp(src_path, dst_path, shallow=False): | |
return True, None | |
# If files are different and diff is needed, generate it | |
if show_diff: | |
with open(src_path, 'r') as src, open(dst_path, 'r') as dst: | |
diff = list(unified_diff( | |
dst.readlines(), | |
src.readlines(), | |
fromfile=str(dst_path), | |
tofile=str(src_path) | |
)) | |
return False, ''.join(diff) | |
return False, None | |
# Iterate through all installed apps | |
for app_config in apps.get_app_configs(): | |
if verbosity >= 2: | |
print(f"Checking app: {app_config.name}") | |
app_path = Path(app_config.path) | |
templates_dir = app_path / 'templates' | |
if templates_dir.exists(): | |
print(f"{templates_dir=}") | |
if not templates_dir.exists(): | |
if verbosity >= 2: | |
print(f"No templates directory found in {app_config.name}") | |
continue | |
# Walk through all template files in the app | |
for template_path in templates_dir.rglob('*.html'): | |
if template_path.is_file(): | |
# Calculate relative path to maintain directory structure | |
relative_path = template_path.relative_to(templates_dir) | |
destination_path = dest_dir / relative_path | |
# Check if destination exists and compare contents | |
if destination_path.exists(): | |
identical, diff = compare_templates(template_path, destination_path) | |
if identical: | |
if verbosity >= 1: | |
print( | |
f"Skipping {relative_path} (identical content)" | |
) | |
templates_identical += 1 | |
continue | |
if not overwrite: | |
if verbosity >= 1: | |
print( | |
f"Skipping {relative_path} (exists with different content)" | |
) | |
if diff: | |
print("Differences:") | |
print(diff) | |
templates_different += 1 | |
templates_skipped += 1 | |
continue | |
if verbosity >= 1: | |
action = 'Would copy' if dry_run else 'Copying' | |
if destination_path.exists(): | |
action = f"{action} (overwriting)" | |
print(f"{action} {relative_path}") | |
if not dry_run: | |
# Create parent directories if they don't exist | |
destination_path.parent.mkdir(parents=True, exist_ok=True) | |
shutil.copy2(template_path, destination_path) | |
templates_copied += 1 | |
else: | |
templates_copied += 1 | |
# Output summary | |
summary = f""" | |
Template copy summary: | |
Templates copied: {templates_copied} | |
Templates skipped: {templates_skipped} | |
Templates identical: {templates_identical} | |
Templates with different content: {templates_different} | |
{'(Dry run - no files were actually copied)' if dry_run else ''} | |
""" | |
print(summary) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment