Code

Ticket #16575: widgets.py

File widgets.py, 3.9 KB (added by anonymous, 3 years ago)
Line 
1"""
2Extra HTML Widget classes
3"""
4
5import time
6import datetime
7import re
8
9from django.forms.widgets import Widget, Select
10from django.utils import datetime_safe
11from django.utils.dates import MONTHS
12from django.utils.safestring import mark_safe
13from django.utils.formats import get_format
14from django.conf import settings
15
16__all__ = ('SelectDateWidget',)
17
18RE_DATE = re.compile(r'(\d{4})-(\d\d?)-(\d\d?)$')
19
20def _parse_format(fmt):
21        def pos(s):
22                match = re.search('[' + s + ']', fmt)
23                return match.start() + 1 if match else 0
24       
25        dic = {'day': pos('dj'), 'month': pos('bEFMmNn'), 'year': pos('yY')}
26        return sorted(filter(None, dic.keys()), key=dic.get)
27
28def _parse_date_fmt():
29        return _parse_format(re.subn(r'\\.', '', get_format('DATE_FORMAT'))[0])
30
31
32class SelectDateWidget(Widget):
33        """
34        A Widget that splits date input into three <select> boxes.
35
36        This also serves as an example of a Widget that has more than one HTML
37        element and hence implements value_from_datadict.
38        """
39        names = ('day', 'month', 'year',)
40        none_values_dafault = '---'
41        field_values = ['%s_' + n for n in names]
42        fields = dict(zip(names, field_values))
43        day_field, month_field, year_field = field_values
44
45        def __init__(self, attrs=None, years=None, required=True,
46                        date_format='', none_values=None):
47                # years is an optional list/tuple of years
48                # to use in the "year" select box.
49                self.attrs = attrs or {}
50                self.required = required
51                if not years:
52                        this_year = datetime.date.today().year
53                        years = range(this_year, this_year+10)
54                print list(years)
55                self.years = years
56                self.date_format = _parse_format(date_format)
57                self.none_values = none_values \
58                        or dict([(i, self.none_values_dafault) for i in 'dmy'])
59
60        def render(self, name, value, attrs=None):
61               
62                get_names_dict = lambda l: dict(zip(self.names, l))
63               
64                try:
65                        values_list = [getattr(value, n) for n in self.names]
66                except AttributeError:
67                        values_list = []
68                        if isinstance(value, basestring):
69                                if settings.USE_L10N:
70                                        try:
71                                                input_format = get_format('DATE_INPUT_FORMATS')[0]
72                                                v = datetime.datetime.strptime(value, input_format)
73                                                values_list = [getattr(v, n) for n in self.names]
74                                        except ValueError:
75                                                pass
76                                else:
77                                        match = RE_DATE.match(value)
78                                        if match:
79                                                values_list = (int(v) for v in match.groups())
80                values = get_names_dict(values_list)
81               
82                choices = get_names_dict([
83                        [(i, i) for i in range(1, 32)],
84                        MONTHS.items(),
85                        [(i, i) for i in self.years],
86                ])
87               
88                create_select_short = lambda n: self.create_select(
89                        name, self.fields[n], value, values.get(n), choices[n], n
90                )
91               
92                htmls = get_names_dict([create_select_short(n) for n in self.names])
93               
94                date_format = self.date_format or _parse_date_fmt()
95                output = [htmls[field] for field in date_format]
96                return mark_safe(u'\n'.join(output))
97
98        def id_for_label(cls, id_):
99                field_list = _parse_date_fmt()
100                if field_list:
101                        first_select = field_list[0]
102                else:
103                        first_select = 'month'
104                return '%s_%s' % (id_, first_select)
105        id_for_label = classmethod(id_for_label)
106
107        def value_from_datadict(self, data, files, name):
108                d, m, y = (data.get(self.fields[n] % name) for n in self.names)
109                if y == m == d == "0":
110                        return None
111                if not (y and m and d):
112                        return data.get(name)
113                if not settings.USE_L10N:
114                        return '%s-%s-%s' % (y, m, d)
115                input_format = get_format('DATE_INPUT_FORMATS')[0]
116                try:
117                        date_value = datetime.date(int(y), int(m), int(d))
118                        date_value = datetime_safe.new_date(date_value)
119                        return date_value.strftime(input_format)
120                except ValueError:
121                        return '%s-%s-%s' % (y, m, d)
122
123        def create_select(self, name, field, value, val, choices, date_part='d'):
124                if 'id' in self.attrs:
125                        id_ = self.attrs['id']
126                else:
127                        id_ = 'id_%s' % name
128                if not (self.required and val):
129                        choices.insert(0, (0, self.none_values[date_part[0]]))
130                local_attrs = self.build_attrs(id=field % id_)
131                s = Select(choices=choices)
132                select_html = s.render(field % name, val, local_attrs)
133                return select_html
134