Django template block system for managing CSS and JavaScript resources with automatic deduplication
npx @tessl/cli install tessl/pypi-django-sekizai@3.0.0Django Sekizai provides a template-based block system for managing CSS and JavaScript resources in Django applications. It enables developers to define placeholders where blocks get rendered and allows sub-templates to append content to those blocks from different places in the template hierarchy, with automatic deduplication of content.
pip install django-sekizai'sekizai' to INSTALLED_APPS# Context processor
from sekizai.context_processors import sekizai
# Context class for when no request is available
from sekizai.context import SekizaiContext
# Helper functions
from sekizai.helpers import validate_template, get_namespaces, Watcher, get_varname, get_context
# Template tag functions (advanced usage)
from sekizai.templatetags.sekizai_tags import validate_context, import_processor
# Data structures
from sekizai.data import UniqueSequenceTemplate usage:
{% load sekizai_tags %}# settings.py
INSTALLED_APPS = [
# ... other apps
'sekizai',
]
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'OPTIONS': {
'context_processors': [
# ... other processors
'sekizai.context_processors.sekizai',
],
},
},
]<!-- base.html -->
<!DOCTYPE html>
<html>
<head>
{% load sekizai_tags %}
{% render_block "css" %}{% endrender_block %}
</head>
<body>
{% block content %}{% endblock %}
{% render_block "js" %}{% endrender_block %}
</body>
</html><!-- some_template.html -->
{% extends "base.html" %}
{% load sekizai_tags %}
{% addtoblock "css" %}
<link rel="stylesheet" href="/static/css/special.css">
{% endaddtoblock %}
{% addtoblock "js" %}
<script src="/static/js/special.js"></script>
{% endaddtoblock %}
{% block content %}
<h1>My Page</h1>
{% endblock %}Template tags for managing blocks in Django templates.
# render_block - Render accumulated block content with optional postprocessor
{% render_block "block_name" %}
<!-- Optional content that gets rendered after sekizai data -->
{% endrender_block %}
# With postprocessor function
{% render_block "block_name" postprocessor "module.path.to.function" %}
<!-- Optional content that gets rendered after sekizai data -->
{% endrender_block %}
# addtoblock - Add content to a block with optional preprocessing
{% addtoblock "block_name" %}
<!-- Content to add to the block -->
{% endaddtoblock %}
# With strip flag to remove leading/trailing whitespace
{% addtoblock "block_name" strip %}
<!-- Content to add (whitespace will be stripped) -->
{% endaddtoblock %}
# With preprocessor function
{% addtoblock "block_name" preprocessor "module.path.to.function" %}
<!-- Content to add (will be processed by function) -->
{% endaddtoblock %}
# With both strip and preprocessor
{% addtoblock "block_name" strip preprocessor "module.path.to.function" %}
<!-- Content to add (stripped then processed) -->
{% endaddtoblock %}
# add_data - Add string data directly to a block
{% add_data "block_name" "content_string" %}
# with_data - Access block content as template variable
{% with_data "block_name" as variable_name %}
<!-- variable_name contains list of block content strings -->
{% end_with_data %}Context processor for making sekizai functionality available in templates.
def sekizai(request=None):
"""
Context processor that makes SekizaiDictionary available in templates.
Parameters:
- request: HttpRequest or None, optional HTTP request object
Returns:
Dict[str, UniqueSequence]: Context dictionary with sekizai data structure
"""Alternative context class for use when no request is available.
class SekizaiContext(Context):
"""
Context class that includes sekizai functionality without requiring a request.
Inherits from django.template.Context and automatically includes sekizai data.
"""
def __init__(self, *args, **kwargs):
"""
Initialize context with sekizai data.
Parameters:
- *args: Positional arguments passed to parent Context
- **kwargs: Keyword arguments passed to parent Context
"""Functions for validating templates contain required sekizai blocks.
def validate_template(template, namespaces):
"""
Validate that a template contains all required namespaces.
Parameters:
- template (str): Template name or path
- namespaces (list): List of namespace names to validate
Returns:
bool: True if template is valid, False otherwise
Note: Validation can be disabled with SEKIZAI_IGNORE_VALIDATION = True
"""
def get_namespaces(template):
"""
Get all sekizai namespaces found in a template and its inheritance chain.
Parameters:
- template (str): Template name or path
Returns:
list: List of namespace strings found in template
"""Helper functions for sekizai configuration and settings.
def get_varname():
"""
Get the context variable name used for sekizai data.
Returns:
str: Variable name (default: 'SEKIZAI_CONTENT_HOLDER' or SEKIZAI_VARNAME setting)
"""
def get_context():
"""
Create a basic Django template context with empty template.
Returns:
Context: Django template context with empty template attached
"""Functions for validating template contexts contain sekizai data.
def validate_context(context):
"""
Validate that a template context contains sekizai data.
Parameters:
- context: Django template context
Returns:
bool: True if context is valid, False if invalid but should be ignored
Raises:
TemplateSyntaxError: If context is invalid and template engine is in debug mode
"""Core data structures used by sekizai.
class UniqueSequence(MutableSequence):
"""
Sequence container that automatically prevents duplicate entries.
Implements collections.abc.MutableSequence interface with automatic deduplication.
"""
def __init__(self):
"""Initialize empty sequence."""
def __contains__(self, item):
"""Check if item exists in sequence."""
def __iter__(self):
"""Iterate over sequence items."""
def __getitem__(self, item):
"""Get item by index."""
def __setitem__(self, key, value):
"""Set item at index."""
def __delitem__(self, key):
"""Delete item at index."""
def __len__(self):
"""Get sequence length."""
def insert(self, index, value):
"""
Insert value at index if not already present.
Parameters:
- index (int): Position to insert at
- value: Value to insert (ignored if already exists)
"""Classes for monitoring sekizai data changes, useful for caching scenarios.
class Watcher:
"""
Monitor context for changes to sekizai data for caching purposes.
Note: Assumes you ONLY ADD, NEVER REMOVE data from context.
"""
def __init__(self, context):
"""
Initialize watcher with template context.
Parameters:
- context: Django template context to monitor
"""
@property
def data(self):
"""
Get current sekizai data from context.
Returns:
dict: Current sekizai data
"""
def get_changes(self):
"""
Get changes to sekizai data since watcher initialization.
Returns:
dict: Dictionary of changes with new and modified data
"""Functions for handling pre and post processing of block content.
def import_processor(import_path):
"""
Import and return a processor function by dotted path.
Parameters:
- import_path (str): Dotted import path to processor function
Returns:
callable: Imported processor function
Raises:
TypeError: If import path doesn't contain at least one dot
"""# settings.py
# Add to INSTALLED_APPS
INSTALLED_APPS = [
'sekizai',
# ... other apps
]
# Add context processor
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'OPTIONS': {
'context_processors': [
'sekizai.context_processors.sekizai',
# ... other processors
],
},
},
]# Optional: Custom variable name for sekizai data (default: 'SEKIZAI_CONTENT_HOLDER')
SEKIZAI_VARNAME = 'MY_SEKIZAI_DATA'
# Optional: Disable template validation (default: False)
SEKIZAI_IGNORE_VALIDATION = TrueThe most common use case is managing CSS and JavaScript resources:
<!-- base.html -->
<!DOCTYPE html>
<html>
<head>
{% load sekizai_tags %}
<title>My Site</title>
{% render_block "css" %}
</head>
<body>
{% block content %}{% endblock %}
{% render_block "js" %}{% endrender_block %}
</body>
</html><!-- page_template.html -->
{% extends "base.html" %}
{% load sekizai_tags %}
{% addtoblock "css" %}
<link rel="stylesheet" href="{% static 'css/page-specific.css' %}">
{% endaddtoblock %}
{% addtoblock "js" %}
<script src="{% static 'js/page-specific.js' %}"></script>
{% endaddtoblock %}
{% block content %}
<h1>My Page</h1>
{% endblock %}from sekizai.context import SekizaiContext
from django.template import Template
# Create context without request
context = SekizaiContext({'variable': 'value'})
# Render template
template = Template("{% load sekizai_tags %}{% addtoblock 'css' %}...")
result = template.render(context)from sekizai.helpers import validate_template, get_namespaces
# Check if template has required blocks
is_valid = validate_template('my_template.html', ['css', 'js'])
# Get all blocks used in template
namespaces = get_namespaces('my_template.html')
print(namespaces) # ['css', 'js', 'meta']from sekizai.helpers import Watcher
# Monitor context changes
watcher = Watcher(context)
# ... template rendering that modifies sekizai blocks ...
# Get what changed
changes = watcher.get_changes()
# Use changes for cache invalidationTemplateSyntaxError: Raised when sekizai context processor is not enabled and template engine is in debug mode.
# Solution: Add context processor to settings
TEMPLATES = [
{
'OPTIONS': {
'context_processors': [
'sekizai.context_processors.sekizai',
],
},
},
]TypeError: Raised by import_processor when import path doesn't contain dots.
# Incorrect
import_processor('myfunction') # TypeError
# Correct
import_processor('mymodule.myfunction')