Source code for helper

# -*- coding: utf-8 -*-
import re

from django.core.urlresolvers import reverse, NoReverseMatch
from django.utils.safestring import mark_safe

from crispy_forms.compatibility import string_types
from crispy_forms.layout import Layout
from crispy_forms.layout_slice import LayoutSlice
from crispy_forms.utils import render_field, flatatt, TEMPLATE_PACK
from crispy_forms.exceptions import FormHelpersException


class DynamicLayoutHandler(object):
    def _check_layout(self):
        if self.layout is None:
            raise FormHelpersException("You need to set a layout in your FormHelper")

    def _check_layout_and_form(self):
        self._check_layout()
        if self.form is None:
            raise FormHelpersException("You need to pass a form instance to your FormHelper")

    def all(self):
        """
        Returns all layout objects of first level of depth
        """
        self._check_layout()
        return LayoutSlice(self.layout, slice(0, len(self.layout.fields), 1))

    def filter(self, *LayoutClasses, **kwargs):
        """
        Returns a LayoutSlice pointing to layout objects of type `LayoutClass`
        """
        self._check_layout()
        max_level = kwargs.pop('max_level', 0)
        greedy = kwargs.pop('greedy', False)
        filtered_layout_objects = self.layout.get_layout_objects(LayoutClasses, max_level=max_level, greedy=greedy)

        return LayoutSlice(self.layout, filtered_layout_objects)

    def filter_by_widget(self, widget_type):
        """
        Returns a LayoutSlice pointing to fields with widgets of `widget_type`
        """
        self._check_layout_and_form()
        layout_field_names = self.layout.get_field_names()

        # Let's filter all fields with widgets like widget_type
        filtered_fields = []
        for pointer in layout_field_names:
            if isinstance(self.form.fields[pointer[1]].widget, widget_type):
                filtered_fields.append(pointer)

        return LayoutSlice(self.layout, filtered_fields)

    def exclude_by_widget(self, widget_type):
        """
        Returns a LayoutSlice pointing to fields with widgets NOT matching `widget_type`
        """
        self._check_layout_and_form()
        layout_field_names = self.layout.get_field_names()

        # Let's exclude all fields with widgets like widget_type
        filtered_fields = []
        for pointer in layout_field_names:
            if not isinstance(self.form.fields[pointer[1]].widget, widget_type):
                filtered_fields.append(pointer)

        return LayoutSlice(self.layout, filtered_fields)

    def __getitem__(self, key):
        """
        Return a LayoutSlice that makes changes affect the current instance of the layout
        and not a copy.
        """
        # when key is a string containing the field name
        if isinstance(key, string_types):
            # Django templates access FormHelper attributes using dictionary [] operator
            # This could be a helper['form_id'] access, not looking for a field
            if hasattr(self, key):
                return getattr(self, key)

            self._check_layout()
            layout_field_names = self.layout.get_field_names()

            filtered_field = []
            for pointer in layout_field_names:
                # There can be an empty pointer
                if len(pointer) == 2 and pointer[1] == key:
                    filtered_field.append(pointer)

            return LayoutSlice(self.layout, filtered_field)

        return LayoutSlice(self.layout, key)

    def __setitem__(self, key, value):
        self.layout[key] = value

    def __delitem__(self, key):
        del self.layout.fields[key]

    def __len__(self):
        if self.layout is not None:
            return len(self.layout.fields)
        else:
            return 0


[docs]class FormHelper(DynamicLayoutHandler): """ This class controls the form rendering behavior of the form passed to the `{% crispy %}` tag. For doing so you will need to set its attributes and pass the corresponding helper object to the tag:: {% crispy form form.helper %} Let's see what attributes you can set and what form behaviors they apply to: **form_method**: Specifies form method attribute. You can see it to 'POST' or 'GET'. Defaults to 'POST' **form_action**: Applied to the form action attribute: - Can be a named url in your URLconf that can be executed via the `{% url %}` template tag. \ Example: 'show_my_profile'. In your URLconf you could have something like:: url(r'^show/profile/$', 'show_my_profile_view', name = 'show_my_profile') - It can simply point to a URL '/whatever/blabla/'. **form_id**: Generates a form id for dom identification. If no id provided then no id attribute is created on the form. **form_class**: String containing separated CSS clases to be applied to form class attribute. The form will always have by default 'uniForm' class. **form_tag**: It specifies if <form></form> tags should be rendered when using a Layout. If set to False it renders the form without the <form></form> tags. Defaults to True. **form_error_title**: If a form has `non_field_errors` to display, they are rendered in a div. You can set title's div with this attribute. Example: "Oooops!" or "Form Errors" **formset_error_title**: If a formset has `non_form_errors` to display, they are rendered in a div. You can set title's div with this attribute. **form_style**: Uni-form has two built in different form styles. You can choose your favorite. This can be set to "default" or "inline". Defaults to "default". Public Methods: **add_input(input)**: You can add input buttons using this method. Inputs added using this method will be rendered at the end of the form/formset. **add_layout(layout)**: You can add a `Layout` object to `FormHelper`. The Layout specifies in a simple, clean and DRY way how the form fields should be rendered. You can wrap fields, order them, customize pretty much anything in the form. Best way to add a helper to a form is adding a property named helper to the form that returns customized `FormHelper` object:: from crispy_forms.helper import FormHelper from crispy_forms.layout import Submit class MyForm(forms.Form): title = forms.CharField(_("Title")) @property def helper(self): helper = FormHelper() helper.form_id = 'this-form-rocks' helper.form_class = 'search' helper.add_input(Submit('save', 'save')) [...] return helper You can use it in a template doing:: {% load crispy_forms_tags %} {% crispy form %} """ _form_method = 'post' _form_action = '' _form_style = 'default' form = None form_id = '' form_class = '' layout = None form_tag = True form_error_title = None formset_error_title = None form_show_errors = True render_unmentioned_fields = False render_hidden_fields = False render_required_fields = False _help_text_inline = False _error_text_inline = True html5_required = False form_show_labels = True template = None field_template = None disable_csrf = False label_class = '' label_size = '' field_class = '' def __init__(self, form=None): self.attrs = {} self.inputs = [] if form is not None: self.form = form self.layout = self.build_default_layout(form) def build_default_layout(self, form): return Layout(*form.fields.keys()) def get_form_method(self): return self._form_method def set_form_method(self, method): if method.lower() not in ('get', 'post'): raise FormHelpersException('Only GET and POST are valid in the \ form_method helper attribute') self._form_method = method.lower() # we set properties the old way because we want to support pre-2.6 python form_method = property(get_form_method, set_form_method) def get_form_action(self): try: return reverse(self._form_action) except NoReverseMatch: return self._form_action def set_form_action(self, action): self._form_action = action # we set properties the old way because we want to support pre-2.6 python form_action = property(get_form_action, set_form_action) def get_form_style(self): if self._form_style == "default": return '' if self._form_style == "inline": return 'inlineLabels' def set_form_style(self, style): if style.lower() not in ('default', 'inline'): raise FormHelpersException('Only default and inline are valid in the \ form_style helper attribute') self._form_style = style.lower() form_style = property(get_form_style, set_form_style) def get_help_text_inline(self): return self._help_text_inline def set_help_text_inline(self, flag): self._help_text_inline = flag self._error_text_inline = not flag help_text_inline = property(get_help_text_inline, set_help_text_inline) def get_error_text_inline(self): return self._error_text_inline def set_error_text_inline(self, flag): self._error_text_inline = flag self._help_text_inline = not flag error_text_inline = property(get_error_text_inline, set_error_text_inline) def add_input(self, input_object): self.inputs.append(input_object) def add_layout(self, layout): self.layout = layout
[docs] def render_layout(self, form, context, template_pack=TEMPLATE_PACK): """ Returns safe html of the rendering of the layout """ form.rendered_fields = set() form.crispy_field_template = self.field_template # This renders the specifed Layout strictly html = self.layout.render( form, self.form_style, context, template_pack=template_pack ) # Rendering some extra fields if specified if self.render_unmentioned_fields or self.render_hidden_fields or self.render_required_fields: fields = set(form.fields.keys()) left_fields_to_render = fields - form.rendered_fields for field in left_fields_to_render: if ( self.render_unmentioned_fields or self.render_hidden_fields and form.fields[field].widget.is_hidden or self.render_required_fields and form.fields[field].widget.is_required ): html += render_field( field, form, self.form_style, context, template_pack=template_pack ) # If the user has Meta.fields defined, not included in the layout, # we suppose they need to be rendered if hasattr(form, 'Meta'): if hasattr(form.Meta, 'fields'): current_fields = set(getattr(form, 'fields', [])) meta_fields = set(getattr(form.Meta, 'fields')) fields_to_render = current_fields & meta_fields left_fields_to_render = fields_to_render - form.rendered_fields for field in left_fields_to_render: html += render_field(field, form, self.form_style, context) return mark_safe(html)
[docs] def get_attributes(self, template_pack=TEMPLATE_PACK): """ Used by crispy_forms_tags to get helper attributes """ items = {} items['form_method'] = self.form_method.strip() items['form_tag'] = self.form_tag items['form_style'] = self.form_style.strip() items['form_show_errors'] = self.form_show_errors items['help_text_inline'] = self.help_text_inline items['error_text_inline'] = self.error_text_inline items['html5_required'] = self.html5_required items['form_show_labels'] = self.form_show_labels items['disable_csrf'] = self.disable_csrf items['label_class'] = self.label_class items['field_class'] = self.field_class label_size_match = re.match('col-lg-(\d+)', self.label_class) if label_size_match: try: items['label_size'] = int(label_size_match.groups()[0]) except: pass items['attrs'] = {} if self.attrs: items['attrs'] = self.attrs.copy() if self.form_action: items['attrs']['action'] = self.form_action.strip() if self.form_id: items['attrs']['id'] = self.form_id.strip() if self.form_class: # uni_form TEMPLATE PACK has a uniForm class by default if template_pack == 'uni_form': items['attrs']['class'] = "uniForm %s" % self.form_class.strip() else: items['attrs']['class'] = self.form_class.strip() else: if template_pack == 'uni_form': items['attrs']['class'] = self.attrs.get('class', '') + " uniForm" items['flat_attrs'] = flatatt(items['attrs']) if self.inputs: items['inputs'] = self.inputs if self.form_error_title: items['form_error_title'] = self.form_error_title.strip() if self.formset_error_title: items['formset_error_title'] = self.formset_error_title.strip() for attribute_name, value in self.__dict__.items(): if attribute_name not in items and attribute_name not in ['layout', 'inputs'] and not attribute_name.startswith('_'): items[attribute_name] = value return items

crispy-forms

django-crispy-forms is a Django application that lets you easily build, customize and reuse forms using your favorite CSS framework, without writing template code and without having to take care of annoying details. You are currently looking at the documentation of the development release.

Support

If you love django-crispy-forms, consider making a small donation on Flattr:

Related Topics