django中的一个选择时间的widget

django中的一个选择时间的widget

This snippet defines a Widget that is very similar to the SelectDateWidget located in django.forms.extras.widgets. The main difference however is that it works with Times instead of Dates.

The SelectTimeWidget supports both 24-hr and 12-hr formats, and flexible time increments for hours, minutes and seconds. Sample usage is illustrated below:

# Specify a basic 24-hr time Widget (the default)
t = forms.TimeField(widget=SelectTimeWidget())

# Force minutes and seconds to be displayed in increments of 10
t = forms.TimeField(widget=SelectTimeWidget(minute_step=10, second_step=10))

# Use a 12-hr time format, which will display a 4th select
# element containing a.m. and p.m. options)
t = forms.TimeField(widget=SelectTimeWidget(twelve_hr=True))

UPDATE: Changed the time pattern to a more precise regular expression, and added the sample usage above.


import  re
from  django.forms.widgets  import  Widget, Select, MultiWidget
from  django.forms.extras.widgets  import  SelectDateWidget
from  django.utils.safestring  import  mark_safe

#  Attempt to match many time formats:
#
 Example: "12:34:56 P.M."  matches:
#
 ('12', '34', ':56', '56', 'P.M.', 'P', '.', 'M', '.')
#
 ('12', '34', ':56', '56', 'P.M.')
#
 Note that the colon ":" before seconds is optional, but only if seconds are omitted
#
time_pattern = r'(\d\d?):(\d\d)(:(\d\d))? *((a{1}|A{1}|p{1}|P{1})(\.)?(m{1}|M{1})(\.)?)?$'
time_pattern  =  r ' (\d\d?):(\d\d)(:(\d\d))? *([aApP]\.?[mM]\.?)?$ '   #  w/ Magus's suggestions

RE_TIME 
=  re.compile(time_pattern)
#  The following are just more readable ways to access re.matched groups:
HOURS  =  0
MINUTES 
=   1
SECONDS 
=   3
MERIDIEM 
=   4

class  SelectTimeWidget(Widget):
    
"""
    A Widget that splits time input into <select> elements.
    Allows form to show as 24hr: <hour>:<minute>:<second>,
    or as 12hr: <hour>:<minute>:<second> <am|pm> 
    
    Also allows user-defined increments for minutes/seconds
    
"""
    hour_field 
=   ' %s_hour '
    minute_field 
=   ' %s_minute '
    second_field 
=   ' %s_second '  
    meridiem_field 
=   ' %s_meridiem '
    twelve_hr 
=  False  #  Default to 24hr.
    
    
def   __init__ (self, attrs = None, hour_step = None, minute_step = None, second_step = None, twelve_hr = False):
        
'''
        hour_step, minute_step, second_step are optional step values for
        for the range of values for the associated select element
        twelve_hr: If True, forces the output to be in 12-hr format (rather than 24-hr)
        
'''
        self.attrs 
=  attrs  or  {}
        
        
if  twelve_hr:
            self.twelve_hr 
=  True  #  Do 12hr (rather than 24hr)
            self.meridiem_val  =   ' a.m. '   #  Default to Morning (A.M.)
        
        
if  hour_step  and  twelve_hr:
            self.hours 
=  range( 1 , 13 ,hour_step) 
        
elif  hour_step:  #  24hr, with stepping.
            self.hours  =  range(0, 24 ,hour_step)
        
elif  twelve_hr:  #  12hr, no stepping
            self.hours  =  range( 1 , 13 )
        
else #  24hr, no stepping
            self.hours  =  range(0, 24

        
if  minute_step:
            self.minutes 
=  range(0, 60 ,minute_step)
        
else :
            self.minutes 
=  range(0, 60 )

        
if  second_step:
            self.seconds 
=  range(0, 60 ,second_step)
        
else :
            self.seconds 
=  range(0, 60 )

    
def  render(self, name, value, attrs = None):
        
try #  try to get time values from a datetime.time object (value)
            hour_val, minute_val, second_val  =  value.hour, value.minute, value.second
            
if  self.twelve_hr:
                
if  hour_val  >=   12 :
                    self.meridiem_val 
=   ' p.m. '
                
else :
                    self.meridiem_val 
=   ' a.m. '
        
except  AttributeError:
            hour_val 
=  minute_val  =  second_val  =  0
            
if  isinstance(value, basestring):
                match 
=  RE_TIME.match(value)
                
if  match:
                    time_groups 
=  match.groups();
                    hour_val 
=  int(time_groups[HOURS])  %   24   #  force to range(0-24)
                    minute_val  =  int(time_groups[MINUTES]) 
                    
if  time_groups[SECONDS]  is  None:
                        second_val 
=  0
                    
else :
                        second_val 
=  int(time_groups[SECONDS])
                    
                    
#  check to see if meridiem was passed in
                     if  time_groups[MERIDIEM]  is   not  None:
                        self.meridiem_val 
=  time_groups[MERIDIEM]
                    
else #  otherwise, set the meridiem based on the time
                         if  self.twelve_hr:
                            
if  hour_val  >=   12 :
                                self.meridiem_val 
=   ' p.m. '
                            
else :
                                self.meridiem_val 
=   ' a.m. '
                        
else :
                            self.meridiem_val 
=  None
                    

        
#  If we're doing a 12-hr clock, there will be a meridiem value, so make sure the
         #  hours get printed correctly
         if  self.twelve_hr  and  self.meridiem_val:
            
if  self.meridiem_val.lower().startswith( ' p ' and  hour_val  >   12   and  hour_val  <   24 :
                hour_val 
=  hour_val  %   12
        
elif  hour_val  ==  0:
            hour_val 
=   12
            
        output 
=  []
        
if   ' id '   in  self.attrs:
            id_ 
=  self.attrs[ ' id ' ]
        
else :
            id_ 
=   ' id_%s '   %  name

        
#  NOTE: for times to get displayed correctly, the values MUST be converted to unicode
         #  When Select builds a list of options, it checks against Unicode values
        hour_val  =  u " %.2d "   %  hour_val
        minute_val 
=  u " %.2d "   %  minute_val
        second_val 
=  u " %.2d "   %  second_val

        hour_choices 
=  [( " %.2d " % i,  " %.2d " % i)  for  i  in  self.hours]
        local_attrs 
=  self.build_attrs(id = self.hour_field  %  id_)
        select_html 
=  Select(choices = hour_choices).render(self.hour_field  %  name, hour_val, local_attrs)
        output.append(select_html)

        minute_choices 
=  [( " %.2d " % i,  " %.2d " % i)  for  i  in  self.minutes]
        local_attrs[
' id ' =  self.minute_field  %  id_
        select_html 
=  Select(choices = minute_choices).render(self.minute_field  %  name, minute_val, local_attrs)
        output.append(select_html)

        second_choices 
=  [( " %.2d " % i,  " %.2d " % i)  for  i  in  self.seconds]
        local_attrs[
' id ' =  self.second_field  %  id_
        select_html 
=  Select(choices = second_choices).render(self.second_field  %  name, second_val, local_attrs)
        output.append(select_html)
    
        
if  self.twelve_hr:
            
#   If we were given an initial value, make sure the correct meridiem get's selected.
             if  self.meridiem_val  is   not  None  and   self.meridiem_val.startswith( ' p ' ):
                    meridiem_choices 
=  [( ' p.m. ' , ' p.m. ' ), ( ' a.m. ' , ' a.m. ' )]
            
else :
                meridiem_choices 
=  [( ' a.m. ' , ' a.m. ' ), ( ' p.m. ' , ' p.m. ' )]

            local_attrs[
' id ' =  local_attrs[ ' id ' =  self.meridiem_field  %  id_
            select_html 
=  Select(choices = meridiem_choices).render(self.meridiem_field  %  name, self.meridiem_val, local_attrs)
            output.append(select_html)

        
return  mark_safe(u ' \n ' .join(output))

    
def  id_for_label(self, id_):
        
return   ' %s_hour '   %  id_
    id_for_label 
=  classmethod(id_for_label)

    
def  value_from_datadict(self, data, files, name):
        
#  if there's not h:m:s data, assume zero:
        h  =  data.get(self.hour_field  %  name, 0)  #  hour
        m  =  data.get(self.minute_field  %  name, 0)  #  minute 
        s  =  data.get(self.second_field  %  name, 0)  #  second

        meridiem 
=  data.get(self.meridiem_field  %  name, None)

        
# NOTE: if meridiem IS None, assume 24-hr
         if  meridiem  is   not  None:
            
if  meridiem.lower().startswith( ' p ' and  int(h)  !=   12 :
                h 
=  (int(h) + 12 ) % 24  
            
elif  meridiem.lower().startswith( ' a ' and  int(h)  ==   12 :
                h 
=  0
        
        
if  (int(h)  ==  0  or  h)  and  m  and  s:
            
return   ' %s:%s:%s '   %  (h, m, s)

        
return  data.get(name, None)

你可能感兴趣的:(django中的一个选择时间的widget)