Login

CheckedField

Author:
oggy
Posted:
September 16, 2008
Language:
Python
Version:
1.0
Tags:
checkbox forms combining
Score:
1 (after 1 ratings)

Here's a snippet to pair an arbitrary form Field type (the "target field") with a checkbox. If the checkbox is not selected, the cleaned_data value for the field will be None. If the checkbox is selected, it will return the target field's cleaned value.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# fields.py
from django import forms
from widgets import CheckedWidget

class CheckedField(forms.Field):
    """
    Class which equips the given Field class with an additional checkbox.

    >>> class TestForm1(forms.Form):
    ...     url_field = CheckedField(forms.URLField)
    ...
    >>> TestForm1().as_table()
    u'<tr><th><label for="id_url_field_0">Url field:</label></th><td><input type="checkbox" name="url_field_0" id="id_url_field_0" /><input type="text" name="url_field_1" id="id_url_field_1" /></td></tr>'
    >>> form = TestForm1({})
    >>> form.is_valid()
    True
    >>> form.cleaned_data['url_field'] == CheckedField.UNSELECTED_VALUE
    True
    >>> form = TestForm1({'url_field_0' : 'on'})
    >>> form.is_valid()
    False
    >>> form.errors
    {'url_field': [u'This field is required.']}
    >>> form = TestForm1({'url_field_0' : 'on', 'url_field_1' : 'bogus_url'})
    >>> form.is_valid()
    False
    >>> form.errors
    {'url_field': [u'Enter a valid URL.']}
    >>> form = TestForm1({'url_field_0' : 'on', 'url_field_1' : 'http://www.real.url.com/'})
    >>> form.is_valid()
    True
    >>> form.cleaned_data['url_field']
    u'http://www.real.url.com/'
    >>>

    """
    # the value to use if the checbox is not selected
    UNSELECTED_VALUE = None
    FORM_FIELD_ARGS = ('required', 'widget', 'label', 'initial', 'help_text', 'error_messages')

    def __init__(self, target, *args, **kwargs):
        """
            Target can either be a Field type or an instance of one.

            In the former case, all the extra arguments are passed over to the target field's constructor
        """
        if isinstance(target, type):
            target = target(*args, **kwargs)
        self.field = target
        if 'widget' in kwargs:
            widget = kwargs['widget']
        else:
            widget = self.field.widget
        kwargs['widget'] = CheckedWidget(widget)
        # some keyword arguments make sense only for the "target" Field subclass,
        # not for forms.Field itself
        field_kwargs = dict([(kw, kwargs[kw]) for kw in kwargs if kw in self.FORM_FIELD_ARGS])
        super(CheckedField, self).__init__(*args, **field_kwargs)

    def clean(self, value):
        try:
            cb_value, field_value = value
            if cb_value:
                # checkbox is selected, return the field's value
                return self.field.clean(field_value)
            else:
                # checkbox is not selected, return the default value
                return self.__class__.UNSELECTED_VALUE
        except forms.util.ValidationError, ve:
            # we don't want to swallow ValidationErrors from the "target" field
            raise ve
        except TypeError, ValueError:
            raise forms.util.ValidationError, u'%s is not an iterable, or there is a length mismatch' % unicode(value)

# widgets.py

from django import forms

class CheckedWidget(forms.MultiWidget):
    """ 
    A widget which pairs another widget with a CheckboxInput widget

    The target widget is given as the first argument to the constructor,
    and can be either an instance or a type
    """

    def __init__(self, target, attrs=None):
        if isinstance(target, type):
            target = target(attrs)
        widgets = (forms.CheckboxInput(attrs=attrs), target)
        super(CheckedWidget, self).__init__(widgets, attrs)

    def decompress(self, value):
        """
        If the value is None, assume the CheckboxInput was not selected
        """
        if value is None:
            return [False, None]
        return [True, value]

More like this

  1. Display values from a bound (submitted) form by masida 11 months, 3 weeks ago
  2. MultipleChoiceCommaField by rubic 8 years ago
  3. Widget for CommaSeparatedIntegerField with pre-defined choices by rudyryk 4 years, 8 months ago
  4. MultiSelectField with comma separated values (Field + FormField) by quinode 2 years, 9 months ago
  5. Convert multiple select for m2m to multiple checkboxes in django admin form by abidibo 1 year, 10 months ago

Comments

Please login first before commenting.