Changeset 265

Show
Ignore:
Timestamp:
11/30/06 13:22:32 (2 years ago)
Author:
tim
Message:

added DatePartsSelect? to widgets and also added an example

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/formal/examples/datestimes.py

    r196 r265  
    1010        form = formal.Form() 
    1111        form.addField('isoFormatDate', formal.Date(), formal.TextInput) 
     12        form.addField('datePartsSelect', formal.Date(), formal.widgetFactory(formal.DatePartsSelect, dayFirst=True)) 
    1213        form.addField('monthFirstDate', formal.Date(), formal.DatePartsInput) 
    1314        form.addField('dayFirstDate', formal.Date(), formal.widgetFactory(formal.DatePartsInput, dayFirst=True)) 
  • trunk/formal/widget.py

    r261 r265  
    424424 
    425425 
     426class DatePartsSelect(object): 
     427    """ 
     428    A date entry widget that uses three <input> elements for the day, month and 
     429    year parts. 
     430 
     431    The default entry format is the US (month, day, year) but can be switched to 
     432    the more common (day, month, year) by setting the dayFirst attribute to 
     433    True. 
     434     
     435    The start and end year can be passed through but default to 1970 and 2070. 
     436     
     437    The months default to non-zero prefixed numerics but can be passed as a list 
     438    of label, value pairs 
     439    """     
     440    implements( iformal.IWidget ) 
     441 
     442    dayFirst = False 
     443    days = [ (d,d) for d in xrange(1,32) ] 
     444    months = [ (m,m) for m in xrange(1,13) ] 
     445    yearFrom = 1970 
     446    yearTo = 2070 
     447 
     448    def __init__(self, original, dayFirst=None, yearFrom=None, yearTo=None, months=None): 
     449        self.original = original 
     450        if dayFirst is not None: 
     451            self.dayFirst = dayFirst 
     452        if yearFrom is not None: 
     453            self.yearFrom = yearFrom 
     454        if yearTo is not None: 
     455            self.yearTo = yearTo 
     456        if months is not None: 
     457            self.months = months 
     458 
     459    def _namer(self, prefix): 
     460        def _(part): 
     461            return '%s__%s' % (prefix,part) 
     462        return _ 
     463 
     464    def _renderTag(self, ctx, year, month, day, namer, readonly): 
     465        years = [(v,v) for v in xrange(self.yearFrom,self.yearTo)] 
     466        months = self.months 
     467        days = self.days 
     468 
     469        options = [] 
     470        for value in years: 
     471            if str(value[0]) == year: 
     472                options.append( T.option(value=value[0],selected='selected')[value[1]] ) 
     473            else: 
     474                options.append( T.option(value=value[0])[value[1]] ) 
     475        yearTag = T.select(name=namer('year'))[ options ] 
     476         
     477        options = [] 
     478        for value in months: 
     479            if str(value[0]) == month: 
     480                options.append( T.option(value=value[0],selected='selected')[value[1]] ) 
     481            else: 
     482                options.append( T.option(value=value[0])[value[1]] ) 
     483        monthTag = T.select(name=namer('month'))[ options ] 
     484         
     485         
     486        options = [] 
     487        for value in days: 
     488            if str(value[0]) == day: 
     489                options.append( T.option(value=value[0],selected='selected')[value[1]] ) 
     490            else: 
     491                options.append( T.option(value=value[0])[value[1]] ) 
     492        dayTag = T.select(name=namer('day'))[ options ] 
     493         
     494        if readonly: 
     495            tags = (yearTag, monthTag, dayTag) 
     496            for tag in tags: 
     497                tag(class_='readonly', readonly='readonly') 
     498 
     499        if self.dayFirst: 
     500            return dayTag, ' / ', monthTag, ' / ', yearTag, ' ', _('(day/month/year)') 
     501        else: 
     502            return monthTag, ' / ', dayTag, ' / ', yearTag, ' ', _('(month/day/year)') 
     503 
     504    def render(self, ctx, key, args, errors): 
     505        converter = iformal.IDateTupleConvertible(self.original) 
     506        namer = self._namer(key) 
     507        if errors: 
     508            year = args.get(namer('year'), [''])[0] 
     509            month = args.get(namer('month'), [''])[0] 
     510            day = args.get(namer('day'), [''])[0] 
     511        else: 
     512            year, month, day = converter.fromType(args.get(key)) 
     513 
     514        return self._renderTag(ctx, year, month, day, namer, False) 
     515 
     516    def renderImmutable(self, ctx, key, args, errors): 
     517        converter = iformal.IDateTupleConvertible(self.original) 
     518        namer = self._namer(key) 
     519        year, month, day = converter.fromType(args.get(key)) 
     520        return self._renderTag(ctx, year, month, day, namer, True) 
     521 
     522    def processInput(self, ctx, key, args): 
     523        namer = self._namer(key) 
     524        # Get the form field values as a (y,m,d) tuple 
     525        ymd = [args.get(namer(part), [''])[0].strip() for part in ('year', 'month', 'day')] 
     526        # Remove parts that were not entered. 
     527        ymd = [p for p in ymd if p] 
     528        # Nothing entered means None otherwise we need all three. 
     529        if not ymd: 
     530            ymd = None 
     531        elif len(ymd) != 3: 
     532            raise validation.FieldValidationError("Invalid date") 
     533        # So, we have what looks like a good attempt to enter a date. 
     534        if ymd is not None: 
     535            # Map to integers 
     536            try: 
     537                ymd = [int(p) for p in ymd] 
     538            except ValueError, e: 
     539                raise validation.FieldValidationError("Invalid date") 
     540        ymd = iformal.IDateTupleConvertible(self.original).toType(ymd) 
     541        return self.original.validate(ymd) 
     542     
     543     
    426544class DatePartsInput(object): 
    427545    """ 
     
    10201138    'Checkbox', 'CheckboxMultiChoice', 'CheckedPassword', 'FileUploadRaw', 
    10211139    'Password', 'SelectChoice', 'TextArea', 'TextInput', 'DatePartsInput', 
    1022     'MMYYDatePartsInput', 'Hidden', 'RadioChoice', 'SelectOtherChoice', 
    1023     'FileUpload', 'FileUploadWidget', 
     1140    'DatePartsSelect', 'MMYYDatePartsInput', 'Hidden', 'RadioChoice', 
     1141    'SelectOtherChoice', 'FileUpload', 'FileUploadWidget', 
    10241142    ] 
    10251143