Skip to main content

Standalone template tag for loading webpack manifest files from multiple Django packages/apps

Project description

Django Multi-Manifest Loader

A simple, standalone template tag for loading webpack manifest files from multiple Django packages/apps. Zero dependencies on unmaintained packages.

Problem

When using webpack with multiple Django packages (like righttowork-check, criminalrecords-check, etc.), each package generates its own manifest.json file with hashed asset filenames for cache busting. However, most manifest loaders only read a single manifest file, making it impossible to reference assets from installed packages.

Solution

django-multi-manifest-loader provides a standalone template tag that:

  1. Loads the main webpack manifest (from your dashboard/main app)
  2. Automatically discovers and loads manifest files from all installed Django packages
  3. Namespaces manifests by app to prevent naming collisions
  4. Auto-detects the current app from template context for seamless usage
  5. Makes all assets available via the {% manifest %} template tag
  6. Zero dependencies - doesn't rely on unmaintained packages like django-manifest-loader

Key Features

  • Automatic Namespacing: Each app's manifest is namespaced, so config.js in package1 doesn't conflict with config.js in package2
  • Smart Auto-Detection: Automatically uses the correct app's assets based on which template is rendering
  • Explicit App Parameter: Use app='appname' parameter when you need assets from a specific app
  • Fallback to Main: If an asset isn't found in the current app, falls back to the main app's manifest

Installation

pip install django-multi-manifest-loader

Or install from source:

cd django-multi-manifest-loader
pip install -e .

Usage

1. Add to INSTALLED_APPS

In your settings.py:

INSTALLED_APPS = [
    # ...
    'django_multi_manifest_loader',
    # ...
]

2. Configure (Optional)

In your settings.py:

DJANGO_MULTI_MANIFEST_LOADER = {
    'cache': True,  # Enable caching (default: True in production, False in DEBUG)
    'main_app_name': 'dashboard',  # Name for main app manifests (default: 'main')
}

3. Use in Templates

The manifest tag automatically detects which app's template is rendering and loads the correct assets:

{% load manifest %}

{# In package1/templates/package1/form.html #}
{# Automatically uses package1's manifest #}
<script src="{% manifest 'config.js' %}"></script>
{# Returns: package1/js/config.abc123.js #}

{# In package2/templates/package2/wizard.html #}
{# Automatically uses package2's manifest #}
<script src="{% manifest 'config.js' %}"></script>
{# Returns: package2/js/config.xyz789.js #}

{# Explicit app parameter - use assets from another app #}
<script src="{% manifest 'main.js' app='dashboard' %}"></script>
{# Always returns dashboard's main.js #}

{# If not found in current app, falls back to main app #}
<link href="{% manifest 'shared-styles.css' %}" rel="stylesheet">

How It Works

The loader searches for manifest files in all installed Django apps and namespaces them by app:

  1. Discovery: Finds manifest.json files in all Django apps using staticfiles finders
  2. Namespacing: Each app's manifest is stored under its app name (e.g., package1, package2)
  3. Auto-Detection: When rendering a template, extracts the app name from the template path (e.g., package1/form.htmlpackage1)
  4. Resolution:
    • First tries the current app's manifest
    • Falls back to the main app if not found
    • Supports explicit app='appname' parameter for cross-app asset references

Result: No naming collisions! Each app's config.js is kept separate.

Package Manifest Example

In your Django package (e.g., righttowork-check), generate a manifest using webpack:

webpack.config.js:

const { WebpackManifestPlugin } = require('webpack-manifest-plugin');

module.exports = {
    output: {
        path: path.resolve(__dirname, 'static/righttoworkcheck/js/'),
        filename: '[name].[contenthash:8].js',
        publicPath: 'righttoworkcheck/js/'
    },
    plugins: [
        new WebpackManifestPlugin({
            fileName: '../manifest.json',
            publicPath: 'righttoworkcheck/js/',
        }),
    ],
};

This generates static/righttoworkcheck/manifest.json:

{
  "custom/candidate/wizard/multiple-upload.js": "righttoworkcheck/js/custom/candidate/wizard/multiple-upload.dd215078.js"
}

Configuration

All configuration is optional. Add to your settings.py:

DJANGO_MULTI_MANIFEST_LOADER = {
    # Enable/disable manifest caching
    # Default: True in production (not DEBUG), False in DEBUG mode
    'cache': True,

    # Name for main app manifests (manifests not tied to a specific app)
    # Default: 'main'
    'main_app_name': 'dashboard',

    # Enable debug logging to see which manifests are loaded
    # Default: False
    'debug': False,
}

Configuration Options

Option Type Default Description
cache bool not DEBUG Cache merged manifests in memory. Disable in development for hot reloading.
main_app_name str 'main' Name used for main/shared manifests that aren't tied to a specific app.
debug bool False Enable detailed logging of manifest loading process.

Development

Setup Development Environment

# Clone the repository
cd django-multi-manifest-loader

# Create virtual environment
python3 -m venv .venv

# Activate virtual environment
source .venv/bin/activate  # Linux/Mac
# or
.venv\Scripts\activate  # Windows

# Install package with dev dependencies
pip install -e ".[dev]"

Running Tests

# Run all tests
pytest

# Run with coverage report
pytest --cov=django_multi_manifest_loader --cov-report=term-missing

# Run specific test file
pytest tests/test_manifest_loader.py -v

Code Quality

# Run all quality checks
flake8 django_multi_manifest_loader/ tests/
black --check django_multi_manifest_loader/ tests/
isort --check-only django_multi_manifest_loader/ tests/
ruff check django_multi_manifest_loader/ tests/

# Auto-format code
black django_multi_manifest_loader/ tests/
isort django_multi_manifest_loader/ tests/
ruff check --fix django_multi_manifest_loader/ tests/

Clear Cache Programmatically

During development, you can clear the manifest cache:

from django_multi_manifest_loader import ManifestLoader
ManifestLoader.clear_cache()

Debug Mode

Enable debug mode to see detailed logging:

# settings.py
DJANGO_MULTI_MANIFEST_LOADER = {
    'debug': True,
}

This will log:

  • Number of manifest files found
  • Path to each manifest being loaded
  • Number of entries in each manifest
  • Total merged entries

Hot Reloading in Development

By default, caching is disabled in DEBUG mode (cache=not DEBUG). This means:

  • Production: Manifests are cached for performance
  • Development: Manifests are reloaded on each request for hot reloading

Requirements

  • Python >= 3.10
  • Django >= 4.0

No other dependencies! This package is completely standalone.

How It Works

The loader uses Django's staticfiles finders to discover manifest files:

  1. Main manifest: Searches for manifest.json in static directories
  2. Package manifests: Iterates through all INSTALLED_APPS and checks each for <app_name>/manifest.json
  3. Merging: All found manifests are merged into a single dictionary (later entries override earlier ones)
  4. Caching: Merged manifest is cached in memory (unless disabled)

Publishing

Automated Publishing to PyPI

The package automatically publishes to PyPI when you push a version tag:

# Update version in __init__.py
# Commit changes
git add django_multi_manifest_loader/__init__.py
git commit -m "Bump version to 0.2.0"

# Create and push tag
git tag v0.2.0
git push origin main --tags

This triggers the GitHub Actions workflow which:

  1. Runs all tests across Python 3.10-3.12 and Django 4.2-5.1
  2. Runs all linters (flake8, black, isort, ruff)
  3. Builds the package
  4. Publishes to PyPI using trusted publishing (no API tokens needed)

Manual Publishing

# Build package
python -m build

# Check package
twine check dist/*

# Upload to PyPI (requires PyPI credentials)
twine upload dist/*

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

django_multi_manifest_loader-0.1.3.tar.gz (11.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

django_multi_manifest_loader-0.1.3-py3-none-any.whl (9.1 kB view details)

Uploaded Python 3

File details

Details for the file django_multi_manifest_loader-0.1.3.tar.gz.

File metadata

File hashes

Hashes for django_multi_manifest_loader-0.1.3.tar.gz
Algorithm Hash digest
SHA256 9f69f9833f36680f25fcda3ca935fff8df0e2ff2526046c5ad94454841e859c1
MD5 68e3bc7f8e7f29f97e9f65f9b1b6b10e
BLAKE2b-256 55cd5c5856a9e3407d51a1111c2f9b6fa893e16934536a16b0656d82807249d5

See more details on using hashes here.

Provenance

The following attestation bundles were made for django_multi_manifest_loader-0.1.3.tar.gz:

Publisher: ci.yml on pescheckit/django-multi-manifest-loader

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file django_multi_manifest_loader-0.1.3-py3-none-any.whl.

File metadata

File hashes

Hashes for django_multi_manifest_loader-0.1.3-py3-none-any.whl
Algorithm Hash digest
SHA256 fdbea17d44798cd3d80ab7910b1d1f032d662984f330937622950518844a03d4
MD5 02456a33fc43d8c4669eb570a7e6a3eb
BLAKE2b-256 9b50a452daf66072ee366817dcc3de612078f216cf8aef0b627055e3b6080d53

See more details on using hashes here.

Provenance

The following attestation bundles were made for django_multi_manifest_loader-0.1.3-py3-none-any.whl:

Publisher: ci.yml on pescheckit/django-multi-manifest-loader

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page