summaryrefslogtreecommitdiff
path: root/django/newforms/widgets.py
blob: 5ec27653cff53f5fe219ac3f669df8a166a9024b (plain)
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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
"""
HTML Widget classes
"""

__all__ = (
    'Widget', 'TextInput', 'PasswordInput', 'HiddenInput', 'FileInput',
    'Textarea', 'CheckboxInput',
    'Select', 'SelectMultiple',
)

from django.utils.html import escape
from itertools import chain

try:
    set # Only available in Python 2.4+
except NameError:
    from sets import Set as set # Python 2.3 fallback

# Converts a dictionary to a single string with key="value", XML-style.
# Assumes keys do not need to be XML-escaped.
flatatt = lambda attrs: ' '.join(['%s="%s"' % (k, escape(v)) for k, v in attrs.items()])

class Widget(object):
    requires_data_list = False # Determines whether render()'s 'value' argument should be a list.
    def __init__(self, attrs=None):
        self.attrs = attrs or {}

    def render(self, name, value):
        raise NotImplementedError

class Input(Widget):
    "Base class for all <input> widgets (except type='checkbox', which is special)"
    input_type = None # Subclasses must define this.
    def render(self, name, value, attrs=None):
        if value is None: value = ''
        final_attrs = dict(self.attrs, type=self.input_type, name=name)
        if attrs:
            final_attrs.update(attrs)
        if value != '': final_attrs['value'] = value # Only add the 'value' attribute if a value is non-empty.
        return u'<input %s />' % flatatt(final_attrs)

class TextInput(Input):
    input_type = 'text'

class PasswordInput(Input):
    input_type = 'password'

class HiddenInput(Input):
    input_type = 'hidden'

class FileInput(Input):
    input_type = 'file'

class Textarea(Widget):
    def render(self, name, value, attrs=None):
        if value is None: value = ''
        final_attrs = dict(self.attrs, name=name)
        if attrs:
            final_attrs.update(attrs)
        return u'<textarea %s>%s</textarea>' % (flatatt(final_attrs), escape(value))

class CheckboxInput(Widget):
    def render(self, name, value, attrs=None):
        final_attrs = dict(self.attrs, type='checkbox', name=name)
        if attrs:
            final_attrs.update(attrs)
        if value: final_attrs['checked'] = 'checked'
        return u'<input %s />' % flatatt(final_attrs)

class Select(Widget):
    def __init__(self, attrs=None, choices=()):
        # choices can be any iterable
        self.attrs = attrs or {}
        self.choices = choices

    def render(self, name, value, attrs=None, choices=()):
        if value is None: value = ''
        final_attrs = dict(self.attrs, name=name)
        if attrs:
            final_attrs.update(attrs)
        output = [u'<select %s>' % flatatt(final_attrs)]
        str_value = str(value) # Normalize to string.
        for option_value, option_label in chain(self.choices, choices):
            selected_html = (str(option_value) == str_value) and ' selected="selected"' or ''
            output.append(u'<option value="%s"%s>%s</option>' % (escape(option_value), selected_html, escape(option_label)))
        output.append(u'</select>')
        return u'\n'.join(output)

class SelectMultiple(Widget):
    requires_data_list = True
    def __init__(self, attrs=None, choices=()):
        # choices can be any iterable
        self.attrs = attrs or {}
        self.choices = choices

    def render(self, name, value, attrs=None, choices=()):
        if value is None: value = []
        final_attrs = dict(self.attrs, name=name)
        if attrs:
            final_attrs.update(attrs)
        output = [u'<select multiple="multiple" %s>' % flatatt(final_attrs)]
        str_values = set([str(v) for v in value]) # Normalize to strings.
        for option_value, option_label in chain(self.choices, choices):
            selected_html = (str(option_value) in str_values) and ' selected="selected"' or ''
            output.append(u'<option value="%s"%s>%s</option>' % (escape(option_value), selected_html, escape(option_label)))
        output.append(u'</select>')
        return u'\n'.join(output)

class RadioSelect(Widget):
    pass

class CheckboxSelectMultiple(Widget):
    pass