Custom Template Engine¶
Django IncludeContents provides a custom template engine that extends Django's standard template functionality with additional features while maintaining full compatibility.
Engine Features¶
The custom template engine (includecontents.django.DjangoTemplates) provides:
- HTML Component Syntax: 
<include:component>tags - Multi-line Template Tags: Break long tags across lines
 - Auto-loaded Template Tags: No need for 
{% load includecontents %} - All Standard Django Features: 100% compatibility with existing templates
 
Installation¶
Replace Django's default template backend in your settings.py:
TEMPLATES = [
    {
        'BACKEND': 'includecontents.django.DjangoTemplates',
        'DIRS': [
            BASE_DIR / 'templates',
        ],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]
Architecture¶
Template Processing Pipeline¶
- Template Loading: Standard Django template loading
 - Preprocessing: Convert HTML component syntax to Django tags
 - Multi-line Processing: Handle multi-line template tags
 - Standard Compilation: Use Django's standard template compiler
 - Rendering: Standard Django template rendering
 
Component Discovery¶
The engine automatically discovers components from:
templates/
└── components/
    ├── button.html          → <include:button>
    ├── forms/
    │   └── field.html       → <include:forms:field>
    └── ui/
        ├── card.html        → <include:ui:card>
        └── icons/
            └── star.html    → <include:ui:icons:star>
Template Tag Auto-loading¶
These template tags are automatically available without {% load %}:
includecontents/endincludecontentscontents/endcontentswrapif/wrapelif/wrapelse/endwrapifattrsnotfilter
HTML Component Syntax Processing¶
Syntax Transformation¶
The engine transforms HTML component syntax to standard Django tags:
<!-- Input: HTML component syntax -->
<include:card title="Hello" class="my-card">
    <p>Content</p>
    <content:footer>Footer content</content:footer>
</include:card>
<!-- Transformed to: Django template tags -->
{% includecontents "components/card.html" title="Hello" class="my-card" %}
    <p>Content</p>
    {% contents footer %}Footer content{% endcontents %}
{% endincludecontents %}
Attribute Processing¶
Complex attribute processing handles:
- String literals: 
title="Hello" - Template variables: 
title="{{ title }}" - Template expressions: 
href="{% url 'home' %}" - Shorthand syntax: 
{title}→title="{{ title }}" - Conditional classes: 
class:active="{{ is_active }}" - Boolean attributes: 
disabled→disabled="True" 
Self-closing Components¶
<!-- Self-closing syntax -->
<include:icon name="star" size="24" />
<!-- Transformed to -->
{% includecontents "components/icon.html" name="star" size="24" %}{% endincludecontents %}
Multi-line Tag Processing¶
Line Continuation¶
The engine handles multi-line template tags by:
- Detecting unclosed tags: Tags that span multiple lines
 - Concatenating lines: Join lines until tag is complete
 - Preserving whitespace: Maintain proper spacing in output
 - Error reporting: Accurate line numbers in error messages
 
Example Processing¶
<!-- Input -->
{% if user.is_authenticated 
    and user.is_staff 
    and user.has_perm('admin')
%}
    Content
{% endif %}
<!-- Processed as -->
{% if user.is_authenticated and user.is_staff and user.has_perm('admin') %}
    Content
{% endif %}
Advanced Features¶
Context Processors¶
The engine supports all standard Django context processors and custom ones:
# settings.py
TEMPLATES = [
    {
        'BACKEND': 'includecontents.django.DjangoTemplates',
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
                'myapp.context_processors.custom_processor',
            ],
        },
    },
]
Template Loaders¶
Works with all Django template loaders:
# Custom loader configuration
TEMPLATES = [
    {
        'BACKEND': 'includecontents.django.DjangoTemplates',
        'OPTIONS': {
            'loaders': [
                'django.template.loaders.filesystem.Loader',
                'django.template.loaders.app_directories.Loader',
                ('django.template.loaders.cached.Loader', [
                    'django.template.loaders.filesystem.Loader',
                    'django.template.loaders.app_directories.Loader',
                ]),
            ],
        },
    },
]
Custom Template Tags Integration¶
Custom template tags work seamlessly:
# myapp/templatetags/custom_tags.py
from django import template
register = template.Library()
@register.simple_tag
def custom_component(**kwargs):
    return render_to_string('components/custom.html', kwargs)
<!-- In templates -->
{% load custom_tags %}
<include:card>
    {% custom_component data=my_data %}
</include:card>
Performance Considerations¶
Template Caching¶
- Preprocessing is cached: HTML component syntax transformation is cached
 - Standard Django caching: All Django template caching mechanisms work
 - Development vs Production: Use cached loader in production
 
Memory Usage¶
- Minimal overhead: Engine adds minimal memory overhead
 - Template parsing: Slightly more parsing for HTML components
 - Context isolation: Each component gets isolated context (small overhead)
 
Optimization Tips¶
# Production settings
TEMPLATES = [
    {
        'BACKEND': 'includecontents.django.DjangoTemplates',
        'OPTIONS': {
            'loaders': [
                ('django.template.loaders.cached.Loader', [
                    'django.template.loaders.filesystem.Loader',
                    'django.template.loaders.app_directories.Loader',
                ]),
            ],
        },
    },
]
Debugging¶
Debug Information¶
Enable template debugging for detailed error information:
# settings.py
DEBUG = True
TEMPLATES = [
    {
        'BACKEND': 'includecontents.django.DjangoTemplates',
        'OPTIONS': {
            'debug': True,
        },
    },
]
Error Messages¶
The engine provides enhanced error messages:
TemplateSyntaxError: Invalid component syntax in template 'home.html' at line 15:
<include:card title="Hello>
              ^
Expected closing quote for attribute 'title'
Template Source Maps¶
Line numbers in errors map to original template source:
TemplateSyntaxError at /
Missing required prop 'title' for component 'card'
Template: home.html
Line: 23 (original: 23)
Component: components/card.html
Compatibility¶
Django Versions¶
- ✅ Django 3.2 LTS
 - ✅ Django 4.0
 - ✅ Django 4.1
 - ✅ Django 4.2 LTS
 - ✅ Django 5.0
 
Template Engine Compatibility¶
Django Template Engine Specific
This custom template engine is designed specifically for Django templates. For Jinja2 users, similar functionality is provided through the IncludeContentsExtension which offers:
- HTML component syntax via preprocessing
 - Multi-line tag support (native in Jinja2)
 - Context isolation and props system
 - Template tag functionality
 
Both engines provide equivalent features with different implementation approaches.
Third-party Packages¶
The engine works with popular Django packages:
- Django REST Framework: API serialization with component templates
 - Django Crispy Forms: Form rendering in components
 - Django Compressor: Asset compression with component assets
 - Django Debug Toolbar: Full debugging support
 - Django Extensions: Template debugging tools
 
Migration from Standard Engine¶
Migrating is seamless - all existing templates work unchanged:
# Before
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        # ... rest of config
    },
]
# After
TEMPLATES = [
    {
        'BACKEND': 'includecontents.django.DjangoTemplates',
        # ... same config - everything else unchanged
    },
]
Customization¶
Engine Subclassing¶
Extend the engine for custom behavior:
# myapp/engine.py
from includecontents.django import DjangoTemplates
class CustomEngine(DjangoTemplates):
    def __init__(self, params):
        super().__init__(params)
        # Custom initialization
    def get_template(self, template_name):
        # Custom template loading logic
        return super().get_template(template_name)
Custom Component Discovery¶
Override component path resolution:
class CustomEngine(DjangoTemplates):
    def resolve_component_template(self, component_name):
        # Custom component resolution logic
        if component_name.startswith('admin:'):
            return f'admin/components/{component_name[6:]}.html'
        return super().resolve_component_template(component_name)
Testing¶
Unit Testing Templates¶
Test templates using Django's test client:
from django.test import TestCase
from django.template import Template, Context
class TemplateTest(TestCase):
    def test_component_rendering(self):
        template = Template('<include:card title="Test">Content</include:card>')
        html = template.render(Context({}))
        self.assertIn('Test', html)
        self.assertIn('Content', html)
Integration Testing¶
Test full template rendering:
from django.test import TestCase, Client
class ComponentIntegrationTest(TestCase):
    def setUp(self):
        self.client = Client()
    def test_page_with_components(self):
        response = self.client.get('/page-with-components/')
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, 'expected-component-output')
Best Practices¶
1. Gradual Migration¶
Start using HTML components in new templates:
<!-- New templates: Use HTML component syntax -->
<include:card title="New Feature">
    Modern component syntax
</include:card>
<!-- Existing templates: Keep working as-is -->
{% load includecontents %}
{% includecontents "components/card.html" title="Existing" %}
    Legacy syntax still works
{% endincludecontents %}
2. Component Organization¶
Organize components logically:
templates/components/
├── ui/              # UI components
│   ├── button.html
│   ├── card.html
│   └── modal.html
├── forms/           # Form components
│   ├── field.html
│   └── fieldset.html
├── layout/          # Layout components
│   ├── header.html
│   ├── footer.html
│   └── sidebar.html
└── content/         # Content components
    ├── article.html
    └── summary.html
3. Development Workflow¶
# Development settings
TEMPLATES = [
    {
        'BACKEND': 'includecontents.django.DjangoTemplates',
        'OPTIONS': {
            'debug': True,
            'string_if_invalid': 'INVALID_VARIABLE_%s',
        },
    },
]
4. Production Configuration¶
# Production settings
TEMPLATES = [
    {
        'BACKEND': 'includecontents.django.DjangoTemplates',
        'OPTIONS': {
            'debug': False,
            'loaders': [
                ('django.template.loaders.cached.Loader', [
                    'django.template.loaders.filesystem.Loader',
                    'django.template.loaders.app_directories.Loader',
                ]),
            ],
        },
    },
]
Troubleshooting¶
Common Issues¶
Issue: Components not found
Solution: Ensure component files are intemplates/components/ directory.
Issue: Multi-line tags not parsing
Solution: Check for missing closing tags or unbalanced parentheses.Issue: HTML syntax not working
Solution: Verify you're using the custom engine, not standard Django engine.Performance Issues¶
Monitor template rendering performance:
# Add to middleware for debugging
import time
from django.template.response import TemplateResponse
class TemplateTimingMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response
    def __call__(self, request):
        start_time = time.time()
        response = self.get_response(request)
        if isinstance(response, TemplateResponse):
            render_time = time.time() - start_time
            print(f"Template render time: {render_time:.3f}s")
        return response
Next Steps¶
- Learn Component Patterns for advanced usage
 - Explore Integration Guides for tooling setup
 - Check the API Reference for complete engine details