Code

Ticket #6231: ticket_6231_v3.patch

File ticket_6231_v3.patch, 26.0 KB (added by Bernd Schlapsi, 5 years ago)

integrated patch for ticket #9124

  • django/forms/extras/widgets.py

     
    66import re 
    77 
    88from django.forms.widgets import Widget, Select 
    9 from django.utils.dates import MONTHS 
     9from django.utils.dates import MONTHS, MONTHS_AP, MONTHS_3 
    1010from django.utils.safestring import mark_safe 
     11from django.conf import settings 
    1112 
    12 __all__ = ('SelectDateWidget',) 
     13__all__ = ('SelectDateWidget', 'SelectTimeWidget',) 
    1314 
    14 RE_DATE = re.compile(r'(\d{4})-(\d\d?)-(\d\d?)$') 
    1515 
    16 class SelectDateWidget(Widget): 
     16class SelectDateWidgetBase(Widget): 
    1717    """ 
    18     A Widget that splits date input into three <select> boxes. 
     18    Base class for SelectDateWidget, SelectDateTimeWidget and 
     19    SelectTimeWidget. 
     20    """ 
     21    def __init__(self, attrs=None, format=None, required=True): 
     22        if attrs is None: 
     23            attrs = {} 
     24        self.attrs = attrs         
     25        self.values = {} 
     26        self.format = self.parse_format(format) 
     27        self.required= required  
    1928 
    20     This also serves as an example of a Widget that has more than one HTML 
    21     element and hence implements value_from_datadict. 
     29    def render(self, name, value, attrs=None): 
     30        """ 
     31        Return the html code of the widget. 
     32        """ 
     33        if 'id' in self.attrs: 
     34            id_ = self.attrs['id'] 
     35        else: 
     36            id_ = 'id_%s' % name 
     37        local_attrs = self.build_attrs() 
     38        self.values = self.parse_value(value) 
     39        output = [] 
     40        for (n, fmt) in self.format: 
     41            select_name = '%s_%s' % (name, n) 
     42            local_attrs['id'] = '%s_%s' % (id_, n) 
     43            if hasattr(self, '%s_choices' % n): 
     44                choices=getattr(self, '%s_choices' % n)(fmt) 
     45                if not self.required or not self.values[n]: 
     46                    choices.insert(0, (-1, '---')) 
     47                select = Select(choices=choices) 
     48                html = select.render(select_name, self.values[n], local_attrs) 
     49                output.append(html) 
     50        return mark_safe(u'\n'.join(output)) 
     51 
     52    def id_for_label(self, id_): 
     53        return '%s_%s' % (self.format[0][1], id_) 
     54    id_for_label = classmethod(id_for_label) 
     55 
     56    def value_from_datadict(self, data, files, name): 
     57        raise NotImplementedError('SelectDateWidgetBase::value_from_datadict()\ 
     58                is abstract and must be implemented in child classes') 
     59 
     60    def parse_format(self, fmt): 
     61        raise NotImplementedError('SelectDateWidgetBase::parse_format() is \ 
     62                abstract and must be implemented in child classes') 
     63 
     64    def parse_value(self, fmt): 
     65        raise NotImplementedError('SelectDateWidgetBase::parse_value() is \ 
     66                abstract and must be implemented in child classes') 
     67 
     68 
     69class SelectDateWidget(SelectDateWidgetBase): 
    2270    """ 
    23     month_field = '%s_month' 
    24     day_field = '%s_day' 
    25     year_field = '%s_year' 
    26  
    27     def __init__(self, attrs=None, years=None): 
     71    A Widget that splits date input into three <select> boxes. 
     72    """ 
     73    def __init__(self, attrs=None, years=None, format=None, required=True): 
    2874        # years is an optional list/tuple of years to use in the "year" select box. 
    29         self.attrs = attrs or {} 
    3075        if years: 
    3176            self.years = years 
    3277        else: 
    3378            this_year = datetime.date.today().year 
    3479            self.years = range(this_year, this_year+10) 
     80        super(SelectDateWidget, self).__init__(attrs, format, required) 
    3581 
    36     def render(self, name, value, attrs=None): 
    37         try: 
    38             year_val, month_val, day_val = value.year, value.month, value.day 
    39         except AttributeError: 
    40             year_val = month_val = day_val = None 
    41             if isinstance(value, basestring): 
    42                 match = RE_DATE.match(value) 
    43                 if match: 
    44                     year_val, month_val, day_val = [int(v) for v in match.groups()] 
     82    def value_from_datadict(self, data, files, name): 
     83        vals = [] 
     84        y = data.get('%s_year' % name) 
     85        m = data.get('%s_month' % name) 
     86        d = data.get('%s_day' % name) 
     87        if y == m == d == '-1':  
     88            return None  
     89        if y and m and d: 
     90            return u'-'.join([y, m, d]) 
     91        return data.get(name, None) 
    4592 
    46         output = [] 
    47  
    48         if 'id' in self.attrs: 
    49             id_ = self.attrs['id'] 
     93    def parse_value(self, val): 
     94        ret = {} 
     95        if isinstance(val, datetime.date): 
     96            ret['month'] = val.month 
     97            ret['day'] = val.day 
     98            ret['year'] = val.year 
    5099        else: 
    51             id_ = 'id_%s' % name 
     100            try: 
     101                l = map(int, val.split('-')) 
     102            except (ValueError, AttributeError): 
     103                l = (None, None, None) 
     104            for i, k in [(0, 'year'), (1, 'month'), (2, 'day')]: 
     105                try: 
     106                    ret[k] = l[i] 
     107                except IndexError: 
     108                    ret[k] = None 
     109        return ret 
    52110 
    53         month_choices = MONTHS.items() 
    54         month_choices.sort() 
    55         local_attrs = self.build_attrs(id=self.month_field % id_) 
    56         select_html = Select(choices=month_choices).render(self.month_field % name, month_val, local_attrs) 
    57         output.append(select_html) 
     111    def parse_format(self, fmt): 
     112        """ 
     113        Parse the given format `fmt` and set the format property. 
     114        """ 
     115        if fmt is None: 
     116            fmt = settings.DATE_FORMAT 
     117        ret = [] 
     118        for item in fmt: 
     119            if item in ['d', 'D', 'j', 'L']: 
     120                ret.append(('day', item,)) 
     121            elif item in ['n', 'm', 'F', 'b', 'M', 'N']: 
     122                ret.append(('month', item,)) 
     123            elif item in ['y', 'Y']: 
     124                ret.append(('year', item,)) 
     125        return ret 
    58126 
    59         day_choices = [(i, i) for i in range(1, 32)] 
    60         local_attrs['id'] = self.day_field % id_ 
    61         select_html = Select(choices=day_choices).render(self.day_field % name, day_val, local_attrs) 
    62         output.append(select_html) 
     127    def month_choices(self, fmt): 
     128        """ 
     129        Return list of choices (tuple (key, value)) for monthes select. 
     130        """ 
     131        if fmt == 'n': 
     132            # month numbers without leading 0 (1 .. 12) 
     133            return [(i, i) for i in range(1, 13)] 
     134        elif fmt == 'm': 
     135            # month numbers with leading 0 (01 .. 12) 
     136            return [(i, '%02d' % i) for i in range(1, 13)] 
     137        elif fmt in ['F', 'b', 'M', 'N']: 
     138            if fmt == 'F': 
     139                # full month names 
     140                month_choices = MONTHS.items() 
     141            elif fmt == 'b': 
     142                # 3 first letters of month lowercase 
     143                month_choices = [(k, v.lower()) for (k, v) in MONTHS_3.items()] 
     144            elif fmt == 'M': 
     145                # 3 first letters of month 
     146                month_choices = MONTHS_3.items() 
     147            elif fmt == 'N': 
     148                # abbrev of month names 
     149                month_choices = MONTHS_AP.items() 
     150            month_choices.sort() 
     151            return month_choices 
     152        return [] 
    63153 
    64         year_choices = [(i, i) for i in self.years] 
    65         local_attrs['id'] = self.year_field % id_ 
    66         select_html = Select(choices=year_choices).render(self.year_field % name, year_val, local_attrs) 
    67         output.append(select_html) 
     154    def day_choices(self, fmt): 
     155        """ 
     156        Return list of choices (tuple (key, value)) for days select. 
     157        """ 
     158        if fmt == 'j': 
     159            # day of month number without leading 0 
     160            return [(i, i) for i in range(1, 32)] 
     161        elif fmt == 'd': 
     162            # day of month number with leading 0 
     163            return [(i, '%02d' % i) for i in range(1, 32)] 
     164        return [] 
    68165 
    69         return mark_safe(u'\n'.join(output)) 
     166    def year_choices(self, fmt): 
     167        """ 
     168        Return list of choices (tuple (key, value)) for years select. 
     169        """ 
     170        if fmt == 'Y': 
     171            # years with 4 numbers 
     172            return [(i, i) for i in self.years] 
     173        elif fmt == 'y': 
     174            # years with only the last 2 numbers 
     175            return [(i, str(i)[-2:]) for i in self.years] 
     176        return [] 
    70177 
    71     def id_for_label(self, id_): 
    72         return '%s_month' % id_ 
    73     id_for_label = classmethod(id_for_label) 
    74178 
     179class SelectTimeWidget(SelectDateWidgetBase): 
     180    """ 
     181    A Widget that splits time input into two or three <select> boxes. 
     182    XXX: at the moment it is limited to theses formats: 'Hi' and 'His'. 
     183    """ 
     184    def __init__(self, attrs=None, format=None, required=True): 
     185        super(SelectTimeWidget, self).__init__(attrs, format, required) 
     186 
     187    def parse_format(self, fmt): 
     188        if fmt not in ['Hi', 'His']: 
     189            fmt = 'Hi' 
     190        ret = [] 
     191        for item in fmt: 
     192            if item == 'H': 
     193                ret.append(('hour', item,)) 
     194            elif item == 'i': 
     195                ret.append(('minute', item,)) 
     196            elif item == 's': 
     197                ret.append(('second', item,)) 
     198        return ret 
     199 
     200    def parse_value(self, val): 
     201        ret = {} 
     202        if isinstance(val, datetime.time): 
     203            ret['hour'] = val.hour 
     204            ret['minute'] = val.minute 
     205            ret['second'] = val.second 
     206        else: 
     207            try: 
     208                l = map(int, val.split(':')) 
     209            except (ValueError, AttributeError): 
     210                l = (None, None, None) 
     211            for i, k in [(0, 'hour'), (1, 'minute'), (2, 'second')]: 
     212                try: 
     213                    ret[k] = l[i] 
     214                except IndexError: 
     215                    ret[k] = None 
     216        return ret 
     217 
    75218    def value_from_datadict(self, data, files, name): 
    76         y, m, d = data.get(self.year_field % name), data.get(self.month_field % name), data.get(self.day_field % name) 
    77         if y and m and d: 
    78             return '%s-%s-%s' % (y, m, d) 
     219        vals = [] 
     220        h = data.get('%s_hour' % name) 
     221        m = data.get('%s_minute' % name) 
     222        s = data.get('%s_second' % name) 
     223        if h and m: 
     224            if s: 
     225                return u':'.join([h, m, s]) 
     226            else: 
     227                return u':'.join([h, m]) 
    79228        return data.get(name, None) 
     229 
     230    def hour_choices(self, fmt): 
     231        """ 
     232        Return list of choices (tuple (key, value)) for hours select. 
     233        """ 
     234        # hour 24H format with leading 0 
     235        return [(i, '%02d' % i) for i in range(0, 24)] 
     236 
     237    def minute_choices(self, fmt): 
     238        """ 
     239        Return list of choices (tuple (key, value)) for minutes select. 
     240        """ 
     241        # minutes with leading 0 
     242        return [(i, '%02d' % i) for i in range(0, 60)] 
     243 
     244    def second_choices(self, fmt): 
     245        """ 
     246        Return list of choices (tuple (key, value)) for seconds select. 
     247        """ 
     248        # seconds with leading 0 
     249        return [(i, '%02d' % i) for i in range(0, 60)] 
     250 
  • tests/regressiontests/forms/extra.py

     
    2020# SelectDateWidget ############################################################ 
    2121 
    2222>>> from django.forms.extras import SelectDateWidget 
    23 >>> w = SelectDateWidget(years=('2007','2008','2009','2010','2011','2012','2013','2014','2015','2016')) 
     23>>> w = SelectDateWidget(years=('2007','2008','2009','2010','2011','2012','2013','2014','2015','2016'), format='FjY') 
    2424>>> print w.render('mydate', '') 
    2525<select name="mydate_month" id="id_mydate_month"> 
     26<option value="-1">---</option> 
    2627<option value="1">January</option> 
    2728<option value="2">February</option> 
    2829<option value="3">March</option> 
     
    3738<option value="12">December</option> 
    3839</select> 
    3940<select name="mydate_day" id="id_mydate_day"> 
     41<option value="-1">---</option>  
    4042<option value="1">1</option> 
    4143<option value="2">2</option> 
    4244<option value="3">3</option> 
     
    7072<option value="31">31</option> 
    7173</select> 
    7274<select name="mydate_year" id="id_mydate_year"> 
     75<option value="-1">---</option>  
    7376<option value="2007">2007</option> 
    7477<option value="2008">2008</option> 
    7578<option value="2009">2009</option> 
     
    233236>>> print b.cleaned_data['mydate'] 
    2342372008-04-01 
    235238 
     239>>> w = SelectDateWidget(years=('2007','2008','2009','2010','2011','2012','2013','2014','2015','2016'), required=False) 
     240>>> print w.render('mydate', '') 
     241<select name="mydate_month" id="id_mydate_month"> 
     242<option value="-1">---</option> 
     243<option value="1">Jan.</option> 
     244<option value="2">Feb.</option> 
     245<option value="3">March</option> 
     246<option value="4">April</option> 
     247<option value="5">May</option> 
     248<option value="6">June</option> 
     249<option value="7">July</option> 
     250<option value="8">Aug.</option> 
     251<option value="9">Sept.</option> 
     252<option value="10">Oct.</option> 
     253<option value="11">Nov.</option> 
     254<option value="12">Dec.</option> 
     255</select> 
     256<select name="mydate_day" id="id_mydate_day"> 
     257<option value="-1">---</option> 
     258<option value="1">1</option> 
     259<option value="2">2</option> 
     260<option value="3">3</option> 
     261<option value="4">4</option> 
     262<option value="5">5</option> 
     263<option value="6">6</option> 
     264<option value="7">7</option> 
     265<option value="8">8</option> 
     266<option value="9">9</option> 
     267<option value="10">10</option> 
     268<option value="11">11</option> 
     269<option value="12">12</option> 
     270<option value="13">13</option> 
     271<option value="14">14</option> 
     272<option value="15">15</option> 
     273<option value="16">16</option> 
     274<option value="17">17</option> 
     275<option value="18">18</option> 
     276<option value="19">19</option> 
     277<option value="20">20</option> 
     278<option value="21">21</option> 
     279<option value="22">22</option> 
     280<option value="23">23</option> 
     281<option value="24">24</option> 
     282<option value="25">25</option> 
     283<option value="26">26</option> 
     284<option value="27">27</option> 
     285<option value="28">28</option> 
     286<option value="29">29</option> 
     287<option value="30">30</option> 
     288<option value="31">31</option> 
     289</select> 
     290<select name="mydate_year" id="id_mydate_year"> 
     291<option value="-1">---</option> 
     292<option value="2007">2007</option> 
     293<option value="2008">2008</option> 
     294<option value="2009">2009</option> 
     295<option value="2010">2010</option> 
     296<option value="2011">2011</option> 
     297<option value="2012">2012</option> 
     298<option value="2013">2013</option> 
     299<option value="2014">2014</option> 
     300<option value="2015">2015</option> 
     301<option value="2016">2016</option> 
     302</select> 
     303>>> print w.render('mydate', '2010-04-15') 
     304<select name="mydate_month" id="id_mydate_month"> 
     305<option value="-1">---</option> 
     306<option value="1">Jan.</option> 
     307<option value="2">Feb.</option> 
     308<option value="3">March</option> 
     309<option value="4" selected="selected">April</option> 
     310<option value="5">May</option> 
     311<option value="6">June</option> 
     312<option value="7">July</option> 
     313<option value="8">Aug.</option> 
     314<option value="9">Sept.</option> 
     315<option value="10">Oct.</option> 
     316<option value="11">Nov.</option> 
     317<option value="12">Dec.</option> 
     318</select> 
     319<select name="mydate_day" id="id_mydate_day"> 
     320<option value="-1">---</option> 
     321<option value="1">1</option> 
     322<option value="2">2</option> 
     323<option value="3">3</option> 
     324<option value="4">4</option> 
     325<option value="5">5</option> 
     326<option value="6">6</option> 
     327<option value="7">7</option> 
     328<option value="8">8</option> 
     329<option value="9">9</option> 
     330<option value="10">10</option> 
     331<option value="11">11</option> 
     332<option value="12">12</option> 
     333<option value="13">13</option> 
     334<option value="14">14</option> 
     335<option value="15" selected="selected">15</option> 
     336<option value="16">16</option> 
     337<option value="17">17</option> 
     338<option value="18">18</option> 
     339<option value="19">19</option> 
     340<option value="20">20</option> 
     341<option value="21">21</option> 
     342<option value="22">22</option> 
     343<option value="23">23</option> 
     344<option value="24">24</option> 
     345<option value="25">25</option> 
     346<option value="26">26</option> 
     347<option value="27">27</option> 
     348<option value="28">28</option> 
     349<option value="29">29</option> 
     350<option value="30">30</option> 
     351<option value="31">31</option> 
     352</select> 
     353<select name="mydate_year" id="id_mydate_year"> 
     354<option value="-1">---</option> 
     355<option value="2007">2007</option> 
     356<option value="2008">2008</option> 
     357<option value="2009">2009</option> 
     358<option value="2010" selected="selected">2010</option> 
     359<option value="2011">2011</option> 
     360<option value="2012">2012</option> 
     361<option value="2013">2013</option> 
     362<option value="2014">2014</option> 
     363<option value="2015">2015</option> 
     364<option value="2016">2016</option> 
     365</select> 
    236366 
     367 
     368# SelectTimeWidget ############################################################ 
     369 
     370>>> from django.forms.extras import SelectTimeWidget 
     371>>> w = SelectTimeWidget() 
     372>>> print w.render('mytime', '') 
     373<select name="mytime_hour" id="id_mytime_hour"> 
     374<option value="-1">---</option> 
     375<option value="0">00</option> 
     376<option value="1">01</option> 
     377<option value="2">02</option> 
     378<option value="3">03</option> 
     379<option value="4">04</option> 
     380<option value="5">05</option> 
     381<option value="6">06</option> 
     382<option value="7">07</option> 
     383<option value="8">08</option> 
     384<option value="9">09</option> 
     385<option value="10">10</option> 
     386<option value="11">11</option> 
     387<option value="12">12</option> 
     388<option value="13">13</option> 
     389<option value="14">14</option> 
     390<option value="15">15</option> 
     391<option value="16">16</option> 
     392<option value="17">17</option> 
     393<option value="18">18</option> 
     394<option value="19">19</option> 
     395<option value="20">20</option> 
     396<option value="21">21</option> 
     397<option value="22">22</option> 
     398<option value="23">23</option> 
     399</select> 
     400<select name="mytime_minute" id="id_mytime_minute"> 
     401<option value="-1">---</option> 
     402<option value="0">00</option> 
     403<option value="1">01</option> 
     404<option value="2">02</option> 
     405<option value="3">03</option> 
     406<option value="4">04</option> 
     407<option value="5">05</option> 
     408<option value="6">06</option> 
     409<option value="7">07</option> 
     410<option value="8">08</option> 
     411<option value="9">09</option> 
     412<option value="10">10</option> 
     413<option value="11">11</option> 
     414<option value="12">12</option> 
     415<option value="13">13</option> 
     416<option value="14">14</option> 
     417<option value="15">15</option> 
     418<option value="16">16</option> 
     419<option value="17">17</option> 
     420<option value="18">18</option> 
     421<option value="19">19</option> 
     422<option value="20">20</option> 
     423<option value="21">21</option> 
     424<option value="22">22</option> 
     425<option value="23">23</option> 
     426<option value="24">24</option> 
     427<option value="25">25</option> 
     428<option value="26">26</option> 
     429<option value="27">27</option> 
     430<option value="28">28</option> 
     431<option value="29">29</option> 
     432<option value="30">30</option> 
     433<option value="31">31</option> 
     434<option value="32">32</option> 
     435<option value="33">33</option> 
     436<option value="34">34</option> 
     437<option value="35">35</option> 
     438<option value="36">36</option> 
     439<option value="37">37</option> 
     440<option value="38">38</option> 
     441<option value="39">39</option> 
     442<option value="40">40</option> 
     443<option value="41">41</option> 
     444<option value="42">42</option> 
     445<option value="43">43</option> 
     446<option value="44">44</option> 
     447<option value="45">45</option> 
     448<option value="46">46</option> 
     449<option value="47">47</option> 
     450<option value="48">48</option> 
     451<option value="49">49</option> 
     452<option value="50">50</option> 
     453<option value="51">51</option> 
     454<option value="52">52</option> 
     455<option value="53">53</option> 
     456<option value="54">54</option> 
     457<option value="55">55</option> 
     458<option value="56">56</option> 
     459<option value="57">57</option> 
     460<option value="58">58</option> 
     461<option value="59">59</option> 
     462</select> 
     463>>> w = SelectTimeWidget(required=False) 
     464>>> print w.render('mytime', '15:45') 
     465<select name="mytime_hour" id="id_mytime_hour"> 
     466<option value="-1">---</option> 
     467<option value="0">00</option> 
     468<option value="1">01</option> 
     469<option value="2">02</option> 
     470<option value="3">03</option> 
     471<option value="4">04</option> 
     472<option value="5">05</option> 
     473<option value="6">06</option> 
     474<option value="7">07</option> 
     475<option value="8">08</option> 
     476<option value="9">09</option> 
     477<option value="10">10</option> 
     478<option value="11">11</option> 
     479<option value="12">12</option> 
     480<option value="13">13</option> 
     481<option value="14">14</option> 
     482<option value="15" selected="selected">15</option> 
     483<option value="16">16</option> 
     484<option value="17">17</option> 
     485<option value="18">18</option> 
     486<option value="19">19</option> 
     487<option value="20">20</option> 
     488<option value="21">21</option> 
     489<option value="22">22</option> 
     490<option value="23">23</option> 
     491</select> 
     492<select name="mytime_minute" id="id_mytime_minute"> 
     493<option value="-1">---</option> 
     494<option value="0">00</option> 
     495<option value="1">01</option> 
     496<option value="2">02</option> 
     497<option value="3">03</option> 
     498<option value="4">04</option> 
     499<option value="5">05</option> 
     500<option value="6">06</option> 
     501<option value="7">07</option> 
     502<option value="8">08</option> 
     503<option value="9">09</option> 
     504<option value="10">10</option> 
     505<option value="11">11</option> 
     506<option value="12">12</option> 
     507<option value="13">13</option> 
     508<option value="14">14</option> 
     509<option value="15">15</option> 
     510<option value="16">16</option> 
     511<option value="17">17</option> 
     512<option value="18">18</option> 
     513<option value="19">19</option> 
     514<option value="20">20</option> 
     515<option value="21">21</option> 
     516<option value="22">22</option> 
     517<option value="23">23</option> 
     518<option value="24">24</option> 
     519<option value="25">25</option> 
     520<option value="26">26</option> 
     521<option value="27">27</option> 
     522<option value="28">28</option> 
     523<option value="29">29</option> 
     524<option value="30">30</option> 
     525<option value="31">31</option> 
     526<option value="32">32</option> 
     527<option value="33">33</option> 
     528<option value="34">34</option> 
     529<option value="35">35</option> 
     530<option value="36">36</option> 
     531<option value="37">37</option> 
     532<option value="38">38</option> 
     533<option value="39">39</option> 
     534<option value="40">40</option> 
     535<option value="41">41</option> 
     536<option value="42">42</option> 
     537<option value="43">43</option> 
     538<option value="44">44</option> 
     539<option value="45" selected="selected">45</option> 
     540<option value="46">46</option> 
     541<option value="47">47</option> 
     542<option value="48">48</option> 
     543<option value="49">49</option> 
     544<option value="50">50</option> 
     545<option value="51">51</option> 
     546<option value="52">52</option> 
     547<option value="53">53</option> 
     548<option value="54">54</option> 
     549<option value="55">55</option> 
     550<option value="56">56</option> 
     551<option value="57">57</option> 
     552<option value="58">58</option> 
     553<option value="59">59</option> 
     554</select> 
     555>>> w = SelectTimeWidget(required=True) 
     556>>> print w.render('mytime', '15:45') 
     557<select name="mytime_hour" id="id_mytime_hour"> 
     558<option value="0">00</option> 
     559<option value="1">01</option> 
     560<option value="2">02</option> 
     561<option value="3">03</option> 
     562<option value="4">04</option> 
     563<option value="5">05</option> 
     564<option value="6">06</option> 
     565<option value="7">07</option> 
     566<option value="8">08</option> 
     567<option value="9">09</option> 
     568<option value="10">10</option> 
     569<option value="11">11</option> 
     570<option value="12">12</option> 
     571<option value="13">13</option> 
     572<option value="14">14</option> 
     573<option value="15" selected="selected">15</option> 
     574<option value="16">16</option> 
     575<option value="17">17</option> 
     576<option value="18">18</option> 
     577<option value="19">19</option> 
     578<option value="20">20</option> 
     579<option value="21">21</option> 
     580<option value="22">22</option> 
     581<option value="23">23</option> 
     582</select> 
     583<select name="mytime_minute" id="id_mytime_minute"> 
     584<option value="0">00</option> 
     585<option value="1">01</option> 
     586<option value="2">02</option> 
     587<option value="3">03</option> 
     588<option value="4">04</option> 
     589<option value="5">05</option> 
     590<option value="6">06</option> 
     591<option value="7">07</option> 
     592<option value="8">08</option> 
     593<option value="9">09</option> 
     594<option value="10">10</option> 
     595<option value="11">11</option> 
     596<option value="12">12</option> 
     597<option value="13">13</option> 
     598<option value="14">14</option> 
     599<option value="15">15</option> 
     600<option value="16">16</option> 
     601<option value="17">17</option> 
     602<option value="18">18</option> 
     603<option value="19">19</option> 
     604<option value="20">20</option> 
     605<option value="21">21</option> 
     606<option value="22">22</option> 
     607<option value="23">23</option> 
     608<option value="24">24</option> 
     609<option value="25">25</option> 
     610<option value="26">26</option> 
     611<option value="27">27</option> 
     612<option value="28">28</option> 
     613<option value="29">29</option> 
     614<option value="30">30</option> 
     615<option value="31">31</option> 
     616<option value="32">32</option> 
     617<option value="33">33</option> 
     618<option value="34">34</option> 
     619<option value="35">35</option> 
     620<option value="36">36</option> 
     621<option value="37">37</option> 
     622<option value="38">38</option> 
     623<option value="39">39</option> 
     624<option value="40">40</option> 
     625<option value="41">41</option> 
     626<option value="42">42</option> 
     627<option value="43">43</option> 
     628<option value="44">44</option> 
     629<option value="45" selected="selected">45</option> 
     630<option value="46">46</option> 
     631<option value="47">47</option> 
     632<option value="48">48</option> 
     633<option value="49">49</option> 
     634<option value="50">50</option> 
     635<option value="51">51</option> 
     636<option value="52">52</option> 
     637<option value="53">53</option> 
     638<option value="54">54</option> 
     639<option value="55">55</option> 
     640<option value="56">56</option> 
     641<option value="57">57</option> 
     642<option value="58">58</option> 
     643<option value="59">59</option> 
     644</select> 
     645 
     646Accepts a datetime or a string: 
     647 
     648>>> w.render('mydate', datetime.time(15, 45, 15)) == w.render('mydate', '15:45:15') 
     649True 
     650 
     651Using a SelectDateWidget in a form: 
     652 
     653>>> class GetTime(Form): 
     654...     mytime = TimeField(widget=SelectTimeWidget) 
     655>>> a = GetTime({'mytime_hour':'15', 'mytime_minute':'45', 'mytime_second':'15'}) 
     656>>> print a.is_valid() 
     657True 
     658>>> print a.cleaned_data['mytime'] 
     65915:45:15 
     660 
     661As with any widget that implements get_value_from_datadict, 
     662we must be prepared to accept the input from the "as_hidden" 
     663rendering as well. 
     664 
     665>>> print a['mytime'].as_hidden() 
     666<input type="hidden" name="mytime" value="15:45:15" id="id_mytime" /> 
     667>>> b=GetTime({'mytime':'15:45:15'}) 
     668>>> print b.is_valid() 
     669True 
     670>>> print b.cleaned_data['mytime'] 
     67115:45:15 
     672 
     673 
    237674# MultiWidget and MultiValueField ############################################# 
    238675# MultiWidgets are widgets composed of other widgets. They are usually 
    239676# combined with MultiValueFields - a field that is composed of other fields.