Django template tags and filters for customizing form field rendering without modifying Python form definitions.
npx @tessl/cli install tessl/pypi-django-widget-tweaks@1.5.0Django Widget Tweaks provides template-level control over form field rendering without requiring Python-level form definitions. It offers template tags and filters for customizing HTML attributes, CSS classes, and field properties directly in Django templates, enabling clean separation between presentation logic and business logic.
pip install django-widget-tweaks'widget_tweaks' to INSTALLED_APPS in settings.py# Load in Django templates
{% load widget_tweaks %}{% load widget_tweaks %}
<!-- Basic attribute modification -->
{{ form.name|attr:"placeholder:Enter your name" }}
<!-- Add CSS classes -->
{{ form.email|add_class:"form-control email-input" }}
<!-- Use render_field tag for HTML-like syntax -->
{% render_field form.message rows="5" cols="40" placeholder="Your message" %}
<!-- Conditional styling based on field state -->
{{ form.password|add_error_class:"error-border"|add_required_class:"required-field" }}Django Widget Tweaks operates through Django's template system using two main approaches:
render_field tag for more intuitive field customizationThe library works by copying Django BoundField objects and decorating their as_widget method to inject custom attributes, ensuring the original form definitions remain unchanged.
Core functionality for setting, appending, and removing HTML attributes on Django form fields.
# Template filters for attribute manipulation
{{ field|attr:"attribute:value" }} # Set or replace HTML attribute
{{ field|append_attr:"attribute:value" }} # Append to existing attribute
{{ field|remove_attr:"attribute" }} # Remove HTML attribute
{{ field|set_data:"key:value" }} # Set HTML5 data attribute (data-key="value")Usage Examples:
<!-- Set input type -->
{{ form.search|attr:"type:search" }}
<!-- Add placeholder and set multiple attributes -->
{{ form.email|attr:"placeholder:user@example.com"|attr:"autocomplete:email" }}
<!-- Remove unwanted attributes -->
{{ form.field|remove_attr:"readonly" }}
<!-- Set data attributes for JavaScript -->
{{ form.price|set_data:"validation:currency"|set_data:"min:0" }}
<!-- Append to existing classes or attributes -->
{{ form.description|append_attr:"class:large-text"|append_attr:"style:min-height: 100px;" }}Specialized filters for managing CSS classes with conditional application based on field state.
# CSS class manipulation filters
{{ field|add_class:"css_class" }} # Add CSS class(es)
{{ field|add_label_class:"css_class" }} # Add CSS class to field label
{{ field|add_error_class:"css_class" }} # Add class only if field has errors
{{ field|add_required_class:"css_class" }} # Add class only if field is required
{{ field|add_error_attr:"attribute:value" }} # Set attribute only if field has errorsUsage Examples:
<!-- Basic class addition -->
{{ form.username|add_class:"form-control" }}
<!-- Multiple classes -->
{{ form.title|add_class:"form-control large-input highlighted" }}
<!-- Conditional classes based on field state -->
{{ form.email|add_error_class:"is-invalid"|add_required_class:"required" }}
<!-- Style labels -->
{{ form.description|add_label_class:"form-label text-bold" }}
<!-- Accessibility attributes on errors -->
{{ form.password|add_error_attr:"aria-invalid:true" }}Template tag providing intuitive HTML-like syntax for field customization with support for both assignment and appending operations.
# render_field template tag
{% render_field field attribute="value" attribute+="append_value" %}Usage Examples:
<!-- Basic field rendering with attributes -->
{% render_field form.title class="form-control" placeholder="Enter title" %}
<!-- Append to existing attributes -->
{% render_field form.description class+=" large-textarea" rows="8" %}
<!-- Change input types -->
{% render_field form.search type="search" %}
{% render_field form.phone type="tel" %}
<!-- Template variables as values -->
{% render_field form.message placeholder=form.message.label %}
<!-- Vue.js style attributes with double colon -->
{% render_field form.status v-bind::class="{active:isActive}" %}
<!-- Mix of assignment and appending -->
{% render_field form.tags class="tag-input" class+=" autocomplete" data-source="/api/tags" %}Context Variables for render_field:
# Special template context variables that affect render_field behavior
WIDGET_ERROR_CLASS # CSS class automatically applied to fields with errors
WIDGET_REQUIRED_CLASS # CSS class automatically applied to required fieldsContext Variable Usage:
{% with WIDGET_ERROR_CLASS='error-field' WIDGET_REQUIRED_CLASS='required-field' %}
{% render_field form.email type="email" class="form-control" %}
{% render_field form.password type="password" class="form-control" %}
{% render_field form.confirm_password type="password" class="form-control" %}
{% endwith %}Utility filters for determining field and widget types, useful for conditional template logic and CSS styling.
# Field type inspection filters
{{ field|field_type }} # Returns field class name in lowercase (e.g., "charfield")
{{ field|widget_type }} # Returns widget class name in lowercase (e.g., "textinput")Usage Examples:
<!-- Dynamic CSS classes based on field type -->
<div class="field {{ field|field_type }} {{ field|widget_type }} {{ field.html_name }}">
{{ field }}
</div>
<!-- Conditional logic based on field type -->
{% if field|field_type == "charfield" %}
{{ field|attr:"maxlength:255" }}
{% elif field|field_type == "emailfield" %}
{{ field|attr:"type:email" }}
{% endif %}
<!-- Widget-specific styling -->
{% if field|widget_type == "textarea" %}
{{ field|add_class:"auto-resize" }}
{% elif field|widget_type == "select" %}
{{ field|add_class:"custom-select" }}
{% endif %}Django Widget Tweaks filters can be chained together, with leftmost filters taking precedence (useful for creating reusable templates with overridable defaults):
<!-- Leftmost filter wins -->
{{ form.title|attr:"class:default-class"|attr:"class:override-class" }}
<!-- Result: class="default-class" -->
<!-- Reusable field template with defaults -->
{# inc/field.html #}
{% load widget_tweaks %}
<div class="field-wrapper">
{{ field|add_class:"form-control"|attr:"data-default:true" }}
</div>
{# Usage with overrides #}
{% include "inc/field.html" with field=form.email|attr:"data-default:false"|add_class:"email-field" %}The render_field tag can be combined with filters for complex field customization:
{% render_field form.category|append_attr:"readonly:readonly" type="text" placeholder="Category" %}Leverage Django's form validation states for dynamic styling:
<!-- Comprehensive field with all states -->
{{ form.username|add_class:"form-control"|add_error_class:"is-invalid"|add_required_class:"required"|add_error_attr:"aria-describedby:username-error" }}
{% if form.username.errors %}
<div id="username-error" class="invalid-feedback">
{{ form.username.errors.0 }}
</div>
{% endif %}# Package version (available after installation)
import widget_tweaks
widget_tweaks.__version__ # String version or None if not installed