Include Django Media assets into <head> using template tags
Project description
Django include media
An app for Django that allows templates and views to add Script/Stylesheets to
a page using Django's forms.Media object, with automatic collection and
deduplication, outputting assets into <head>. Inspired by django-sekizai.
Installation
pip install django-include-media
Add to INSTALLED_APPS:
INSTALLED_APPS = [
...
"include_media",
]
Usage
Place {% include_media %} in <head> of your base template. Then use the
use_media templatetag or page_media context to add the assets you need.
All your sub-templates or templates from templatetags can now reliably add assets to the page.
{# base.html #}
{% load include_media_tags %}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
{% include_media %}
</head>
<body>
{% block content %}{% endblock %}
</body>
</html>
Component and template assets
Declare assets inline with {% use_media %}. Assets are deduplicated by
object identity, including the same component twice only renders its
assets once:
{% load include_media_tags %}
{% use_media form.media %}
{% use_media css="myapp/widget.css" %}
{% use_media js="myapp/script.js" %}
Extra HTML attributes can be passed as keyword arguments and are forwarded
to the rendered tag. Add csp_nonce_attr to opt a specific asset into
Django's CSP nonce (Django 6.0+); the nonce is applied if csp_nonce is
present in the template context and is a no-op otherwise:
{% use_media js="myapp/widget.js" type="module" %}
{% use_media js="myapp/widget.js" type="module" csp_nonce_attr %}
{% use_media css="myapp/widget.css" media="print" %}
{% use_media form.media csp_nonce_attr %}
View-level assets
Pass page_media via get_context_data. If a site-wide context processor
also sets page_media, the two are merged automatically:
from django.forms import Media
from django.forms.widgets import Script
from include_media import Stylesheet
from django.views.generic import TemplateView
class DatePickerView(TemplateView):
template_name = "datepicker.html"
def get_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs)
ctx["page_media"] = Media(
css={"all":[Stylesheet("datepicker/datepicker.css")]},
js=[Script("datepicker/datepicker.js", type="module")],
)
return ctx
Site-wide assets
Declare assets required on every page in a context processor:
# myproject/context_processors.py
from django.forms import Media
from django.forms.widgets import Script
from include_media import Stylesheet
def site_media(request):
nonce = getattr(request, "csp_nonce", None)
attrs = {"nonce": nonce} if nonce else {}
media = Media(
css={"all":[Stylesheet("base.css", **attrs)]},
js=[Script("base.js", type="module", **attrs)],
)
if request.user.is_authenticated:
media += Media(js=[Script("dashboard.js", type="module", **attrs)])
return {"page_media": media}
TEMPLATES = [{
"OPTIONS": {
"context_processors": [
...
"myproject.context_processors.site_media",
],
},
}]
App-level registration
Apps can use the register() method to add app level assets that are linked to
a registered_media context_processor.
# myapp/apps.py
from django.apps import AppConfig
class MyAppConfig(AppConfig):
name = "myapp"
def ready(self):
from django.forms import Media
from django.forms.widgets import Script
from include_media import register, Stylesheet
register(Media(
css={"all": [Stylesheet("myapp/base.css")]},
js=[Script("myapp/base.js", type="module")],
))
Add the single bundled context processor to TEMPLATES once, regardless of
how many apps use register():
TEMPLATES = [{
"OPTIONS": {
"context_processors": [
...
"include_media.context_processors.registered_media",
],
},
}]
Import maps
{% include_media %} can generate a <script type="importmap"> tag,
letting templates and reusable components declare their ES module specifiers
in the same places they already declare CSS and JS assets. All entries from
across the template hierarchy are merged into a single importmap tag, placed
before other assets in <head>.
Template tag — for one-off or inline declarations:
{% use_media js="vendor/htmx.js" importmap="htmx" %}
{% use_media js="https://cdn.example.com/lodash.js" importmap="lodash" %}
ImportmapScript — for reusable widgets and forms that need a module
specifier wherever they are used:
from django.forms import Form
from include_media import ImportmapScript
class DatePickerForm(Form):
class Media:
js = [
ImportmapScript("vendor/pikaday.js", specifier="pikaday"),
Script("datepicker/widget.js", type="module"),
]
To hook up a JS build system's manifest, you could add ImportmapScript
entries to page_media from a context processor:
import json
from pathlib import Path
from django.forms import Media
from include_media import ImportmapScript
_manifest = json.loads((BASE_DIR / "static/dist/manifest.json").read_text())
def importmap(request):
return {
"page_media": Media(js=[
ImportmapScript(f"/static/dist/{entry['file']}", specifier=name)
for name, entry in _manifest.items()
])
}
Merging and precedence — all sources (template tags, ImportmapScript in
page_media) are merged into one <script type="importmap"> tag with
first-wins semantics: page_media is processed before template tags, so
view-level declarations take precedence when the same specifier appears in
multiple places.
Asset post-processing
Set INCLUDE_MEDIA_POSTPROCESSOR to a dotted Python path to intercept the
collected asset HTML before it is written to the page. The callable receives
the fully-rendered asset tags — after deduplication and nonce application —
and must return a string:
# settings.py
INCLUDE_MEDIA_POSTPROCESSOR = "myproject.assets.postprocess"
# myproject/assets.py
def postprocess(assets_html: str, context) -> str:
# assets_html contains the rendered <link> / <script> / importmap tags.
# context is the template Context, giving access to request, user, etc.
return assets_html # return a modified string
context is the full Django template Context. Access the current request
via context.get("request") if you need per-request information (user,
headers, etc.).
The setting is validated at startup by Django's system check framework; a misconfigured path or non-callable target raises an error before the first request is served.
Compatibility
- Python 3.10+
- Django 5.2, 6.0+ (
Stylesheetis backported for Django < 6.1)
Contributing
Contributions are welcome! Please feel free to submit a Pull Request. The aim of this repo is to explore this idea and if it feels right to propose it back to django core, where it could be implemented cleaner. Any feedback is welcome.
License
BSD 3-Clause License
Changelog
0.2.0
- Add a post processing hook
- Add an app level register for media assets
0.1.0 — (Initial version)
- Provide template tags
{% include_media %}and{% use_media %}for collecting Django Media assets into<head> - Supports CSS
<link rel="stylesheet">and JavaScript<script>tags and setting any attributes. - Sets correct path with
django.contrib.staticfilesif relevant. - CSP nonce support available via
csp_nonce_attrflag on{% use_media %} - Import map support via
importmap=keyword andImportmapScript - Site-wide media via
page_mediacontext variable; merges with template-level assets {% use_media %}falls back to rendering inline with debug-mode warning, if the include is not there.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file django_include_media-0.2.1.tar.gz.
File metadata
- Download URL: django_include_media-0.2.1.tar.gz
- Upload date:
- Size: 9.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c188256630dd40d444cc6e5356e661ab736a523b85486a68dcc1052d9744e255
|
|
| MD5 |
2c91c01736533e2674451a72677a883b
|
|
| BLAKE2b-256 |
7c7a21f3315a670440274569c31aa505371db041622ef8192fe26ab1af90c395
|
Provenance
The following attestation bundles were made for django_include_media-0.2.1.tar.gz:
Publisher:
pypi-release.yml on blighj/django-include-media
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
django_include_media-0.2.1.tar.gz -
Subject digest:
c188256630dd40d444cc6e5356e661ab736a523b85486a68dcc1052d9744e255 - Sigstore transparency entry: 1789138231
- Sigstore integration time:
-
Permalink:
blighj/django-include-media@b3494cfdd4e31de9627071fe3af15a4b9b127b4a -
Branch / Tag:
refs/tags/v0.2.1 - Owner: https://github.com/blighj
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
pypi-release.yml@b3494cfdd4e31de9627071fe3af15a4b9b127b4a -
Trigger Event:
release
-
Statement type:
File details
Details for the file django_include_media-0.2.1-py3-none-any.whl.
File metadata
- Download URL: django_include_media-0.2.1-py3-none-any.whl
- Upload date:
- Size: 11.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
60f8d23c927819a2c0d96a3870df3bc0f63439961d4b7e88cdabf77c7efae7a2
|
|
| MD5 |
e59d7843d41360e68e14deb20c0733b7
|
|
| BLAKE2b-256 |
3dbf087f698afa3277ed21eb8702bf052545b5709257404438bf33fac0f03580
|
Provenance
The following attestation bundles were made for django_include_media-0.2.1-py3-none-any.whl:
Publisher:
pypi-release.yml on blighj/django-include-media
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
django_include_media-0.2.1-py3-none-any.whl -
Subject digest:
60f8d23c927819a2c0d96a3870df3bc0f63439961d4b7e88cdabf77c7efae7a2 - Sigstore transparency entry: 1789138276
- Sigstore integration time:
-
Permalink:
blighj/django-include-media@b3494cfdd4e31de9627071fe3af15a4b9b127b4a -
Branch / Tag:
refs/tags/v0.2.1 - Owner: https://github.com/blighj
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
pypi-release.yml@b3494cfdd4e31de9627071fe3af15a4b9b127b4a -
Trigger Event:
release
-
Statement type: