--- name: django-templates description: Creates Django 6.0 HTML templates with partials, HTMX integration, and modern best practices. Use for template creation, refactoring, or HTMX endpoints. argument-hint: [template-name] [--partial|--htmx|--base] allowed-tools: Read, Write, Edit, Glob, Grep --- # Django 6.0 HTML Templates Generate production-ready Django 6.0.2 templates with Template Partials, HTMX integration, CSP support, and modern best practices for the league-planner project. ## When to Use - Creating new HTML templates with Django 6.0 features - Refactoring templates to use Template Partials - Building HTMX-powered dynamic components - Setting up base templates with proper block structure - Implementing reusable template fragments ## Prerequisites - Django 6.0+ installed - Templates directory configured in settings - For HTMX: `django-htmx` package (optional but recommended) ## Instructions ### Step 1: Analyze Request Parse `$ARGUMENTS` to determine: - **Template name**: The target template file - **Type flag**: - `--partial`: Create reusable partial fragments - `--htmx`: HTMX-enabled template with partial endpoints - `--base`: Base template with block structure - (none): Standard template ### Step 2: Check Existing Templates ```bash # Find existing templates in league-planner find . -path "*/templates/*.html" -type f ``` Review the project's template structure and naming conventions. ### Step 3: Generate Template Apply the appropriate pattern from the examples below. --- ## Django 6.0 Template Features ### Template Partials (NEW in 6.0) Define reusable fragments without separate files: ```django {# Define a partial #} {% partialdef card %}

{{ title }}

{{ content }}

{% endpartialdef %} {# Render the partial multiple times #} {% partial card %} {% partial card %} ``` **Inline Option** - Render immediately AND save for reuse: ```django {% partialdef filter_controls inline %}
{{ filter_form.as_p }}
{% endpartialdef %} {# Can still reuse later #} {% partial filter_controls %} ``` ### Accessing Partials from Views (HTMX Pattern) Render only a specific partial: ```python # views.py from django.shortcuts import render def update_component(request, pk): obj = MyModel.objects.get(pk=pk) # Render ONLY the partial named "item_row" return render(request, "myapp/list.html#item_row", {"item": obj}) ``` ### forloop.length (NEW in 6.0) Access total loop count: ```django {% for item in items %}
{{ item.name }} ({{ forloop.counter }}/{{ forloop.length }})
{% endfor %} ``` ### querystring Tag Improvements Build query strings cleanly: ```django {# Basic usage #} First Page {# Modify current query params #} Next {# Remove a parameter #} Clear Filter {# Multiple mappings (NEW in 6.0) #} Reset ``` ### CSP Nonce Support For inline scripts with Content Security Policy: ```django {# In settings: add 'django.template.context_processors.csp' #} ``` --- ## Patterns & Best Practices ### Pattern 1: Base Template with Blocks ```django {# templates/base.html #} {% load static %} {% block title %}League Planner{% endblock %} {% block extra_css %}{% endblock %} {% block navbar %} {% include "includes/navbar.html" %} {% endblock %}
{% block messages %} {% if messages %} {% for message in messages %}
{{ message }}
{% endfor %} {% endif %} {% endblock %} {% block content %}{% endblock %}
{% block footer %} {% include "includes/footer.html" %} {% endblock %} {% block extra_js %}{% endblock %} ``` ### Pattern 2: List Template with Partials ```django {# templates/scheduler/scenario_list.html #} {% extends "base.html" %} {% block title %}Szenarien{% endblock %} {% block content %}

Szenarien

{# Define row partial for HTMX updates #} {% partialdef scenario_row %} {{ scenario.name }} {{ scenario.season }} {{ scenario.get_status_display }} Details {% endpartialdef %} {% for scenario in scenarios %} {% partial scenario_row %} {% empty %} {% endfor %}
Name Saison Status Aktionen
Keine Szenarien vorhanden.
{% endblock %} ``` ### Pattern 3: HTMX-Powered Form ```django {# templates/scheduler/scenario_form.html #} {% extends "base.html" %} {% block content %}

{% if scenario.pk %}Szenario bearbeiten{% else %}Neues Szenario{% endif %}

{# Form partial for HTMX validation #} {% partialdef scenario_form inline %}
{% csrf_token %} {% for field in form %}
{{ field }} {% if field.errors %} {{ field.errors.0 }} {% endif %} {% if field.help_text %} {{ field.help_text }} {% endif %}
{% endfor %}
Abbrechen
{% endpartialdef %}
{% endblock %} ``` ### Pattern 4: Detail Page with Inline Editing ```django {# templates/scheduler/scenario_detail.html #} {% extends "base.html" %} {% block content %}
{# Header partial - editable via HTMX #} {% partialdef scenario_header inline %}

{{ scenario.name }}

Saison: {{ scenario.season }} | Status: {{ scenario.get_status_display }} | Erstellt: {{ scenario.created_at|date:"d.m.Y H:i" }}

{% endpartialdef %} {# Games list partial #} {% partialdef games_list %}

Spiele ({{ games|length }}/{{ games|length }})

{% endpartialdef %} {% partial games_list %}
{% endblock %} ``` ### Pattern 5: Modal with Partial ```django {# templates/includes/modal.html #} {% partialdef modal_container %} {% endpartialdef %} ``` ### Pattern 6: Pagination with querystring ```django {# templates/includes/pagination.html #} {% if page_obj.has_other_pages %} {% endif %} ``` --- ## HTMX View Pattern For HTMX endpoints that render partials: ```python # views.py from django.shortcuts import render, get_object_or_404 from django.views.decorators.http import require_http_methods @require_http_methods(["GET"]) def scenario_row(request, pk): """Render single scenario row partial for HTMX.""" scenario = get_object_or_404(Scenario, pk=pk) # Syntax: "template.html#partial_name" return render(request, "scheduler/scenario_list.html#scenario_row", { "scenario": scenario }) @require_http_methods(["DELETE"]) def scenario_delete(request, pk): """Delete scenario and return empty response for HTMX swap.""" scenario = get_object_or_404(Scenario, pk=pk) scenario.delete() # Return empty string - HTMX will remove the row return HttpResponse("") @require_http_methods(["GET", "POST"]) def scenario_edit_header(request, pk): """Inline edit scenario header via HTMX.""" scenario = get_object_or_404(Scenario, pk=pk) if request.method == "POST": form = ScenarioHeaderForm(request.POST, instance=scenario) if form.is_valid(): form.save() # Return the display partial return render(request, "scheduler/scenario_detail.html#scenario_header", { "scenario": scenario }) else: form = ScenarioHeaderForm(instance=scenario) # Return edit form partial return render(request, "scheduler/scenario_edit_header.html", { "scenario": scenario, "form": form }) ``` --- ## Template Organization (league-planner) ``` templates/ ├── base.html # Main base template ├── includes/ │ ├── navbar.html │ ├── footer.html │ ├── pagination.html │ └── modal.html ├── scheduler/ │ ├── scenario_list.html # With partials for rows │ ├── scenario_detail.html # With partials for sections │ ├── scenario_form.html # With form partial │ └── _scenario_row.html # Standalone partial (legacy) ├── draws/ │ └── ... └── qualifiers/ └── ... ``` --- ## Common Pitfalls - **Partial nicht gefunden**: Partial-Namen müssen exakt matchen, keine Leerzeichen - **Context fehlt**: Partials erben den Context - stelle sicher, dass Variablen verfügbar sind - **HTMX Swap-Probleme**: Verwende IDs für präzises Targeting (`hx-target="#element-id"`) - **CSP Violations**: Inline-Styles/Scripts brauchen `nonce="{{ csp_nonce }}"` wenn CSP aktiv - **N+1 in Templates**: Verwende `select_related`/`prefetch_related` in Views, nicht im Template - **forloop.length Performance**: Bei großen Listen kann dies teuer sein (zählt vorab) ## Deprecation Warning ⚠️ **urlize Filter**: Default-Protokoll wechselt von HTTP zu HTTPS in Django 7.0. Setze `URLIZE_ASSUME_HTTPS = True` in settings.py für Vorwärtskompatibilität. --- ## Verification Test the template: ```bash # Check for template syntax errors python manage.py check --deploy # Render template in shell python manage.py shell >>> from django.template.loader import render_to_string >>> html = render_to_string('scheduler/scenario_list.html', {'scenarios': []}) >>> print(html) # Test partial rendering >>> html = render_to_string('scheduler/scenario_list.html#scenario_row', {'scenario': scenario}) ```