Development Guide¶
This guide is for developers who want to contribute to Django IncludeContents or understand its internals.
Quick Start for Contributors¶
Setting Up Development Environment¶
-
Clone the repository:
-
Install development dependencies:
-
Run tests to verify setup:
Development Workflow¶
Making Changes¶
-
Create a feature branch:
-
Make your changes following the project's coding standards
-
Write tests for new functionality
-
Run the test suite:
-
Check code formatting (if applicable):
Testing Guidelines¶
- Write tests for all new features
- Ensure existing tests pass
- Test both template tag and HTML component syntaxes
- Include edge cases and error conditions
Test Structure:
tests/
├── settings.py # Test Django settings
├── templates/ # Test templates
│ └── components/ # Test component templates
├── test_tag.py # Template tag functionality
├── test_component.py # HTML component syntax
├── test_csrf.py # CSRF token propagation
└── test_multiline.py # Multi-line tag support
Creating Documentation¶
Running Documentation Locally¶
-
Install documentation dependencies:
-
Serve documentation locally:
-
Build documentation:
Documentation Guidelines¶
- Update relevant documentation for any user-facing changes
- Include code examples in documentation
- Test documentation examples to ensure they work
- Follow the existing documentation structure
Changelog Management¶
This project uses towncrier for changelog management.
Adding Changelog Entries¶
Create news fragments for your changes in the changes/ directory:
# Create a news fragment for a new feature
# Format: changes/+description.feature.md
echo "Description of the feature" > changes/+my-feature.feature.md
# Other fragment types:
# changes/+fix-name.bugfix.md # Bug fixes
# changes/+docs.doc.md # Documentation improvements
# changes/+remove-name.removal.md # Removals
# changes/+misc-name.misc.md # Miscellaneous
Fragment Naming Conventions¶
- For new features:
+descriptive-name.feature.md - For GitHub issue fixes:
123.bugfix.md(where 123 is the issue number) - For other bug fixes:
+fix-description.bugfix.md - For documentation:
+docs-description.doc.md - For removals:
+remove-description.removal.md - For miscellaneous:
+misc-description.misc.md
Examples¶
# New feature (use + prefix with description):
echo "Add support for Django template tags in component attributes" > changes/+template-tags-in-attributes.feature.md
# GitHub issue fix (use issue number):
echo "Fix component rendering with special characters" > changes/42.bugfix.md
# Other bug fix (use + prefix):
echo "Fix memory leak in template caching" > changes/+fix-memory-leak.bugfix.md
Important: Never edit CHANGES.md directly - it's generated automatically by towncrier during releases.
Architecture Overview¶
Package Structure¶
includecontents/
├── __init__.py # Package initialization
├── templatetags/
│ └── includecontents.py # Core template tag implementation
├── django/
│ ├── __init__.py
│ ├── base.py # Custom template engine base
│ ├── engine.py # Custom Django template engine
│ └── loaders.py # Template loaders
└── next_version.py # Version management
Key Components¶
1. Template Tag (templatetags/includecontents.py)¶
- Core
{% includecontents %}tag implementation - Context isolation logic
- Content block processing
- Props validation
2. Custom Template Engine (django/)¶
- HTML component syntax parsing (
base.py) - Template engine integration (
engine.py) - Custom template loaders (
loaders.py)
3. Testing Framework (tests/)¶
- Comprehensive test suite
- Test templates and components
- Integration and unit tests
Design Principles¶
- Context Isolation: Components run in isolated contexts for predictability
- Backward Compatibility: Template tag syntax always supported
- HTML-like Syntax: Modern component syntax feels familiar
- Django Integration: Works seamlessly with Django's template system
Contributing Guidelines¶
Code Standards¶
- Follow Django coding conventions
- Write comprehensive tests
- Document new features
- Maintain backward compatibility
Pull Request Process¶
- Fork the repository
- Create a feature branch
- Make your changes with tests
- Create changelog fragment
- Submit pull request
Getting Help¶
- GitHub Issues: Bug reports and feature requests
- Discussions: Questions and community help
- Documentation: Complete feature documentation
Troubleshooting Development Issues¶
Common Setup Issues¶
Tests not running:
# Ensure you're in the project directory
cd django-includecontents
# Install in development mode
pip install -e ".[test]"
# Run tests with verbose output
pytest -v
Documentation not building:
# Install docs dependencies
pip install -e ".[docs]"
# Check for missing dependencies
mkdocs build --verbose
Debugging Tips¶
- Use
uv runfor consistent environments - Enable Django template debugging
- Run specific test files for faster feedback
- Use print statements in template tags for debugging
Release Process¶
The release process for Django IncludeContents uses manual GitHub Actions workflows for complete control over releases.
Creating a Release¶
1. Ensure there are changelog fragments for changes (see Changelog Management above)
2. Run the Release workflow:
- Go to the GitHub repository Actions tab
- Select "Release new version" workflow
- Click "Run workflow"
- Choose the version bump type:
patch,minor, ormajor
The release workflow will automatically:
- Calculate the next version number
- Generate changelog from fragments using towncrier
- Commit the updated CHANGES.md
- Create and push a git tag
- Create a GitHub release with release notes
3. Deploy to PyPI:
- Go to the GitHub repository Actions tab
- Select "Publish to PyPI" workflow
- Click "Run workflow"
- Confirm the deployment
Deploy Manually (Alternative)¶
If you need to deploy without using the GitHub Actions workflow:
1. Install dependencies:
2. Get next version number:
# Replace 'patch' with 'minor' or 'major' as needed
export VERSION=$(python -m includecontents.next_version patch)
echo "Next version: $VERSION"
3. Generate release notes:
4. Build full changelog:
5. Commit changelog and create tag:
git config user.name "Your Name"
git config user.email "your.email@example.com"
git commit -am "Update CHANGES for $VERSION"
git tag "v$VERSION" --file=/tmp/changes.txt --cleanup=whitespace
git push --follow-tags
6. Create GitHub release:
7. Deploy to PyPI:
# Ensure you're on the release version
python -m includecontents.next_version
# Publish to PyPI
pdm publish
Manual Deployment
Manual deployment bypasses the automated checks and should only be used in exceptional circumstances. The GitHub Actions workflow is the preferred method.
Release Checklist¶
- [ ] All tests pass
- [ ] Documentation is updated
- [ ] Changelog fragment created in
changes/directory - [ ] "Release new version" workflow completed successfully
- [ ] GitHub release created with correct version and notes
- [ ] "Publish to PyPI" workflow completed successfully
- [ ] Package available on PyPI
- [ ] Release announcement (if applicable)
Changelog¶
This page shows the complete changelog for Django IncludeContents.
The full changelog is maintained in CHANGES.md and is automatically generated using towncrier.
Latest Changes¶
Change Log¶
This log shows interesting changes that happen for each release of django-includecontents.
Version 4.0.1 (2025-09-30)¶
Features¶
- Django Engine now automatically replaces Django's standard template loaders with includecontents custom loaders, ensuring compatibility with third-party packages like django-template-partials while preserving custom Template class functionality and enhanced error messages.
Bugfixes¶
- Fixed Jinja2 props with undefined variables rendering as literal
{{ var }}instead of empty strings.
Misc¶
- Internal refactoring: moved PropDefinition class to shared module for better code reuse between template engines.
Version 4.0 (2025-09-29)¶
Features¶
- Add comprehensive Jinja2 template engine support with full Django template parity for HTML component syntax, including JavaScript framework attributes (@click, v-model, x-data), nested attributes, HTML content blocks, CSRF token handling, consistent escaping behavior, and proper undefined variable rendering
Version 3.1.2 (2025-08-14)¶
Bugfixes¶
- Fix WrapIfNode to properly handle node traversal for Django's template debugging tools. The node now correctly implements get_nodes_by_type() to traverse all dynamic content blocks within wrapif tags.
Version 3.1.1 (2025-08-06)¶
Features¶
- Improved icon error reporting to show all missing or invalid icons at once, making it easier for developers to fix configuration issues in a single pass
Bugfixes¶
- Fixed compatibility issue with Django 5.2 where invalid icons could cause a TypeError in static file serving instead of returning a proper 404 error
Version 3.1 (2025-08-06)¶
Features¶
- Add file-based caching system for Iconify API responses to improve performance and enable offline development. Icons fetched from the Iconify API can now be cached locally and reused in subsequent builds, configured via
api_cache_rootandapi_cache_static_pathsettings. - Icon sprites now preserve
styleattributes containing CSS variables (e.g.,style="fill: var(--icon-color)"), enabling advanced theming and interactive hover effects that work across the shadow DOM boundary. See the new Styling with CSS Variables documentation for examples.
Version 3.0.1 (2025-08-06)¶
Features¶
- Icon sprite build failures now fail loudly with clear error messages instead of silently returning empty SVGs. This makes configuration errors, missing files, and API failures immediately visible during development and deployment.
Bugfixes¶
- Fixed icon symbol IDs to use component names (e.g., "home") instead of full identifiers (e.g., "mdi-home"). Icons are now consistently referenced by their component names in both templates and generated sprites.
Deprecations and Removals¶
- Removed all storage backend classes and configuration. The icon system now uses only in-memory caching with Django's static files system for production serving. This is a breaking change - remove
storageandstorage_optionsfrom yourINCLUDECONTENTS_ICONSsettings.
Misc¶
- Simplified icon system architecture by removing 1,360+ lines of code. Sprites are now cached in memory during development and served from STATIC_ROOT in production via standard Django static files, eliminating the need for complex storage configuration.
Version 3.0 (2025-08-06)¶
Features¶
- Add icon system with automatic SVG sprite generation. Features
{% icon %}template tag and<icon:name>HTML syntax support.
Version 2.5.2 (2025-08-04)¶
Bugfixes¶
- Fix context processor variables not being available in nested HTML components
Version 2.5.1 (2025-08-01)¶
Bugfixes¶
- Fix JavaScript event modifiers like @click.stop not being passed to child components
Version 2.5 (2025-07-25)¶
Features¶
- Add
...attrsspread syntax to forward undefined attributes from parent to child components
Version 2.4.1 (2025-07-24)¶
Bugfixes¶
- Fix object passing in component attributes to preserve actual objects instead of string representations when using pure variable syntax like
deck="{{ deck }}".
Version 2.4 (2025-07-24)¶
Features¶
- Add support for JavaScript framework event attributes like
@click,v-on:,x-on:, and:(binding shorthand) in component attributes - Add support for mixed content in component attributes, allowing combinations of static text and Django template syntax (e.g.,
class="btn {{ variant }}",href="/products/{{ id }}/", and even template tags likeclass="{% if active %}active{% endif %}").
Version 2.3 (2025-07-23)¶
Features¶
- HTML-based components now have access to all context variables provided by context processors, not just the request object and CSRF token
This ensures consistent behavior between HTML components and regular Django templates.
Version 2.2 (2025-07-22)¶
Features¶
- Support multiple space-separated values in enum props (e.g.,
variant="primary icon") to enable combining visual modifiers.
Bugfixes¶
- Fix parsing of multiline closing tags (e.g.,
</include:item\n>) in HTML component syntax.
Version 2.1.1 (2025-07-02)¶
Bugfixes¶
- Fixed self-closing component tags within nested components incorrectly incrementing the nesting level, causing "Unclosed tag" errors.
Version 2.1 (2025-07-02)¶
Features¶
- Add Django template tag support in component attributes. Component attributes now fully support Django template syntax including
{% url %},{{ variables }},{% if %}conditionals, and all other template tags.
<include:ui-button
variant="primary"
href="{% url 'settings' %}"
class="btn {% if large %}btn-lg{% endif %}"
>
Save Settings
</include:ui-button>
Bugfixes¶
- Fix duplicate content block names error when nesting components with same named content blocks
Version 2.0 (2025-07-01)¶
Features¶
- Add HTML-style
<content:name>syntax for named content blocks in components. This provides a more HTML-consistent alternative to{% contents %}tags while maintaining full backwards compatibility. - Add class prepend syntax for component attrs.
Classes can now be prepended with {% attrs class="card &" %} syntax.
- Append
" &"to prepend component classes before user-provided classes - Complements existing
"& "syntax which appends after user classes - Useful when CSS specificity or utility class ordering matters
- Add enum validation for component props.
Props can now be defined with allowed values: {# props variant=primary,secondary,accent #}
- Validates prop values against the allowed list
- Sets both the prop value and a camelCased boolean (e.g.,
variant="primary"andvariantPrimary=True) - Optional enums start with empty:
size=,small,medium,large - Hyphens are camelCased:
dark-mode→variantDarkMode - Add {% wrapif %} template tag for conditional wrapping.
The new {% wrapif %} tag provides a clean way to conditionally wrap content with HTML elements:
- Shorthand syntax:
{% wrapif condition then "tag" attr=value %}content{% endwrapif %} - Full template syntax: Supports
{% contents %}blocks for complex wrappers - Multiple conditions:
{% wrapelif %}and{% wrapelse %}for if/elif/else patterns - Complex conditions: Inherits all Django template operators (and, or, not, comparisons, in)
- Multiple named contents: Support for multiple content blocks in full syntax
- Attribute handling: Proper escaping and boolean attribute support
This reduces template boilerplate and improves readability when conditionally wrapping content.
- Added |not template filter for negating boolean values in conditional class attributes
Version 1.2.1 (2024-11-19)¶
Bugfixes¶
- Make csrf_token work from within components
Version 1.2 (2024-11-12)¶
Features¶
- Added support for Django-style template variables in component attributes:
title="{{ myTitle }}". The old styletitle={myTitle}is still supported but will be deprecated in a future version.
Bugfixes¶
- Short-hand syntax props weren't being taken into account by the required attrs check.
Version 1.1.1 (2024-07-25)¶
Bugfixes¶
- Fix a bug where the component context wasn't being set correctly, especially noticeable inside of a loop. (5)
Version 1.1 (2024-06-03)¶
Bugfixes¶
- Allow attributes with dashes which don't have values. For example,
<include:foo x-data />. (1)
Version 1.0 (2024-05-16)¶
Features¶
- Update the template engine location so that it will be picked up as the standard Django engine when replaced.
Improved Documentation¶
- Fix some grammar.
- Add a note about how to workaround Prettier stripping AlpineJS'
x-dataquotes.
Deprecations and Removals¶
- Since the template engine location has changed, any users of pre 1.0 versions will need to update their Django settings to point to the new location:
Version 0.8 (2024-05-09)¶
Features¶
- Add shorthand attribute syntax (
<include:foo {title}>).
Bugfixes¶
- Fix component context isolation.
Version 0.7 (2024-05-01)¶
Features¶
- Allow self-closing tags. For example,
<include:foo />. - Handle > inside
<include:tags. - Allow kebab-case attributes. For example,
<include:foo x-data="bar" />.
Improved Documentation¶
- Add a note about
pretier-plugin-jinja-template. - Readme improvements.