Skip to content

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

  1. Clone the repository:

    git clone https://github.com/SmileyChris/django-includecontents.git
    cd django-includecontents
    

  2. Install development dependencies:

    # Install package with test dependencies
    pip install -e ".[test]"
    
    # Or install all dependencies (including deployment tools)
    pip install -e ".[test,deploy]"
    

  3. Run tests to verify setup:

    # Run all tests
    pytest
    
    # Run with coverage
    pytest --cov=includecontents
    

Development Workflow

Making Changes

  1. Create a feature branch:

    git checkout -b feature/your-feature-name
    

  2. Make your changes following the project's coding standards

  3. Write tests for new functionality

  4. Run the test suite:

    # Run all tests
    pytest
    
    # Run a specific test file
    pytest tests/test_tag.py
    
    # Run a specific test
    pytest tests/test_tag.py::test_basic -v
    

  5. Check code formatting (if applicable):

    # Format Django/Jinja templates (requires npm)
    npm install
    npx prettier --write "**/{templates,jinja2}/**/*.html"
    

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

  1. Install documentation dependencies:

    pip install -e ".[docs]"
    

  2. Serve documentation locally:

    mkdocs serve
    

  3. Build documentation:

    mkdocs build
    

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

  1. Context Isolation: Components run in isolated contexts for predictability
  2. Backward Compatibility: Template tag syntax always supported
  3. HTML-like Syntax: Modern component syntax feels familiar
  4. 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

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes with tests
  4. Create changelog fragment
  5. 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

  1. Use uv run for consistent environments
  2. Enable Django template debugging
  3. Run specific test files for faster feedback
  4. 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, or major

The release workflow will automatically:

  1. Calculate the next version number
  2. Generate changelog from fragments using towncrier
  3. Commit the updated CHANGES.md
  4. Create and push a git tag
  5. Create a GitHub release with release notes

3. Deploy to PyPI:

Deploy Manually (Alternative)

If you need to deploy without using the GitHub Actions workflow:

1. Install dependencies:

python -m pip install -e .[deploy]
python -m pip install towncrier

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:

towncrier build --draft --version $VERSION

4. Build full changelog:

towncrier build --yes --name "Version" --version $VERSION

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:

gh release create "v$VERSION" --title "$VERSION" --notes-from-tag --verify-tag

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 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 like class="{% 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" and variantPrimary=True)
  • Optional enums start with empty: size=,small,medium,large
  • Hyphens are camelCased: dark-modevariantDarkMode
  • 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 style title={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-data quotes.

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:
TEMPLATES = [
    {
        "BACKEND": "includecontents.django.DjangoTemplates",
        ...
    },
]

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.