Newer
Older
postorius / src / postorius / fieldset_forms.py
@Florian Fuchs Florian Fuchs on 9 Feb 2015 3 KB Bump Copyright Years
# -*- coding: utf-8 -*-
# Copyright (C) 1998-2015 by the Free Software Foundation, Inc.
#
# This file is part of Postorius.
#
# Postorius is free software: you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option)
# any later version.
#
# Postorius is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
# more details.
#
# You should have received a copy of the GNU General Public License along with
# Postorius.  If not, see <http://www.gnu.org/licenses/>.

from django.forms import Form
from django.utils import safestring
from django.forms.forms import BoundField
from django.forms.util import ErrorList


class FieldsetError(Exception):
    pass


class FieldsetForm(Form):
    """
    Extends a standard form and adds fieldsets and the possibililty
    to use as_div for the automatic rendering of form fields. Inspired
    by WTForm.
    """

    def __init__(self, *args, **kwargs):
        """Initialize a FormsetField."""
        super(FieldsetForm, self).__init__(*args, **kwargs)
        # check if the user specified the wished layout of the form
        if hasattr(self, 'Meta') and hasattr(self.Meta, 'layout'):
            msg = "Meta.layout must be iterable"
            assert hasattr(self.Meta.layout, '__getitem__'), msg
            self.layout = self.Meta.layout
        else:
            self.layout = [["All"]]
            self.layout[0][1:] = (self.fields.keys())

    def as_div(self):
        """Render the form as a set of <div>s."""
        output = ""
        # Adding Errors
        try:
            output += str(self.errors["NON_FIELD_ERRORS"])
        except:
            pass
        # Create the fieldsets
        for index in range(len(self.layout)):
            output += self.create_fieldset(self.layout[index])
        return safestring.mark_safe(output)

    def create_fieldset(self, field):
        """
        Create a <fieldset> around a number of field instances.
        field[0] is the name of the fieldset and field[1:] the fields
        it should include.
        """
        # Create the divs in each fieldset by calling create_divs.
        return u'<fieldset><legend>%s</legend>%s</fieldset>' % (
            field[0],
            self.create_divs(field[1:]))

    def create_divs(self, fields):
        """Create a <div> for each field."""
        output = ""
        for field in fields:
            try:
                # create a field instance for the bound field
                field_instance = self.fields[field]
            except KeyError:
                # could not create the instance so throw an exception
                # msg on a separate line since the line got too long
                # otherwise
                msg = "Could not resolve form field '%s'." % field
                raise FieldsetError(msg)
            # create a bound field containing all the necessary fields
            # from the form
            bound_field = BoundField(self, field_instance, field)
            output += '<div class="field %(class)s">%(label)s%(help_text)s%' \
                      '(errors)s%(field)s</div>\n' % \
                      {'class': bound_field.name,
                       'label': bound_field.label,
                       'help_text': bound_field.help_text,
                       'errors': bound_field.errors,
                       'field': unicode(bound_field)}
        return output