Source code for templatetags.crispy_forms_field

    from itertools import izip
except ImportError:
    izip = zip

from django import forms, template
from django.conf import settings
from django.template import Context, loader

from crispy_forms.utils import TEMPLATE_PACK, get_template_pack

register = template.Library()

[docs]def is_checkbox(field): return isinstance(field.field.widget, forms.CheckboxInput)
[docs]def is_password(field): return isinstance(field.field.widget, forms.PasswordInput)
[docs]def is_radioselect(field): return isinstance(field.field.widget, forms.RadioSelect)
[docs]def is_select(field): return isinstance(field.field.widget, forms.Select)
[docs]def is_checkboxselectmultiple(field): return isinstance(field.field.widget, forms.CheckboxSelectMultiple)
[docs]def is_file(field): return isinstance(field.field.widget, forms.FileInput)
[docs]def is_multivalue(field): return isinstance(field.field.widget, forms.MultiWidget)
[docs]def classes(field): """ Returns CSS classes of a field """ return field.widget.attrs.get('class', None)
[docs]def css_class(field): """ Returns widgets class name in lowercase """ return field.field.widget.__class__.__name__.lower()
[docs]def pairwise(iterable): """s -> (s0,s1), (s2,s3), (s4, s5), ...""" a = iter(iterable) return izip(a, a)
[docs]class CrispyFieldNode(template.Node): def __init__(self, field, attrs): self.field = field self.attrs = attrs self.html5_required = 'html5_required'
[docs] def render(self, context): # Nodes are not threadsafe so we must store and look up our instance # variables in the current rendering context first if self not in context.render_context: context.render_context[self] = ( template.Variable(self.field), self.attrs, template.Variable(self.html5_required) ) field, attrs, html5_required = context.render_context[self] field = field.resolve(context) try: html5_required = html5_required.resolve(context) except template.VariableDoesNotExist: html5_required = False # If template pack has been overridden in FormHelper we can pick it from context template_pack = context.get('template_pack', TEMPLATE_PACK) # There are special django widgets that wrap actual widgets, # such as forms.widgets.MultiWidget, admin.widgets.RelatedFieldWidgetWrapper widgets = getattr(field.field.widget, 'widgets', [getattr(field.field.widget, 'widget', field.field.widget)]) if isinstance(attrs, dict): attrs = [attrs] * len(widgets) converters = { 'textinput': 'textinput textInput', 'fileinput': 'fileinput fileUpload', 'passwordinput': 'textinput textInput', } converters.update(getattr(settings, 'CRISPY_CLASS_CONVERTERS', {})) for widget, attr in zip(widgets, attrs): class_name = widget.__class__.__name__.lower() class_name = converters.get(class_name, class_name) css_class = widget.attrs.get('class', '') if css_class: if css_class.find(class_name) == -1: css_class += " %s" % class_name else: css_class = class_name if ( template_pack in ['bootstrap3'] and not is_checkbox(field) and not is_file(field) and not is_multivalue(field) ): css_class += ' form-control' if field.errors: css_class += ' form-control-danger' if ( template_pack in ['bootstrap4'] and not is_checkbox(field) and not is_file(field) and not is_multivalue(field) ): css_class += ' form-control' if field.errors: css_class += ' is-invalid' widget.attrs['class'] = css_class # HTML5 required attribute if html5_required and field.field.required and 'required' not in widget.attrs: if field.field.widget.__class__.__name__ is not 'RadioSelect': widget.attrs['required'] = 'required' for attribute_name, attribute in attr.items(): attribute_name = template.Variable(attribute_name).resolve(context) if attribute_name in widget.attrs: widget.attrs[attribute_name] += " " + template.Variable(attribute).resolve(context) else: widget.attrs[attribute_name] = template.Variable(attribute).resolve(context) return field
[docs]def crispy_field(parser, token): """ {% crispy_field field attrs %} """ token = token.split_contents() field = token.pop(1) attrs = {} # We need to pop tag name, or pairwise would fail token.pop(0) for attribute_name, value in pairwise(token): attrs[attribute_name] = value return CrispyFieldNode(field, attrs)
[docs]def crispy_addon(field, append="", prepend="", form_show_labels=True): """ Renders a form field using bootstrap's prepended or appended text:: {% crispy_addon form.my_field prepend="$" append=".00" %} You can also just prepend or append like so {% crispy_addon form.my_field prepend="$" %} {% crispy_addon form.my_field append=".00" %} """ if field: context = Context({ 'field': field, 'form_show_errors': True, 'form_show_labels': form_show_labels, }) template = loader.get_template('%s/layout/prepended_appended_text.html' % get_template_pack()) context['crispy_prepended_text'] = prepend context['crispy_appended_text'] = append if not prepend and not append: raise TypeError("Expected a prepend and/or append argument") context = context.flatten() return template.render(context)