views:

542

answers:

4

I have two methods that I'm using as custom tags in a template engine:

# Renders a <select> form field
def select_field(options, selected_item, field_name):
    options = [(str(v),str(v)) for v in options]
    html = ['<select name="%s">' % field_name]
    for k,v in options:
     tmp = '<option '
     if k == selected_item:
      tmp += 'selected '
     tmp += 'value="%s">%s</option>' % (k,v)
     html.append(tmp)
    html.append('</select>')
    return '\n'.join(html)

# Renders a collection of <select> fields for datetime values
def datetime_field(current_dt, field_name):
    if current_dt == None:
     current_dt = datetime.datetime.now()
    day = select_field(range(1, 32), current_dt.day, field_name + "_day")
    month = select_field(range(1, 13), current_dt.month, field_name + "_month")
    year = select_field(range(datetime.datetime.now().year, datetime.datetime.now().year + 10), current_dt.year, field_name + "_year")
    hour = select_field(range(1, 13), current_dt.hour, field_name + "_hour")
    minute = select_field(range(1, 60), current_dt.minute, field_name + "_minute")
    period = select_field(['AM', 'PM'], 'AM', field_name + "_period")
    return "\n".join([day, '/', month, '/', year, ' at ', hour, ':', minute, period])

As you can see in the comments, I'm attempting to generate select fields for building a date and time value.

My first question is, how do I make the day, month, hour, and minute ranges use two digits? For example, the code generates "1, 2, 3..." while I would like "01, 02, 03".

Next, when the fields get generated, the values of the supplied datetime are not selected automatically, even though I'm telling the select_field method to add a "selected" attribute when the value is equal to the supplied datetime's value.

Finally, how do I get the 12-hour period identifier (AM/PM) for the supplied datetime? For the moment, I'm simply selecting 'AM' by default, but I would like to supply this value from the datetime itself.

+1  A: 

Question 1:

>>> '%02d' % 2
'02'
>>> '%02d' % 59
'59'
Ali A
+2  A: 

To turn an integer range into two digit strings:

>>> range(13)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
>>> [ '%02d' % i for i in range(13) ]
['00', '01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12']

Then to get the AM/PM indicator:

>>> import datetime
>>> current_dt = datetime.datetime.now()
>>> current_dt
datetime.datetime(2009, 2, 4, 22, 2, 14, 390000)
>>> ['AM','PM'][current_dt.hour>=12]
'PM'

Voila!

A: 
  1. Supply a format string to the select_field method to show leading zeros.

    >>> print "%02i" % (1, )
    01
    
  2. datetime.hour is 0-23, so you have to adapt it when setting the field. This is also the answer on how to detect AM/PM. Make sure to convert it back correctly.

Torsten Marek
+1  A: 

To add to my previous answer, some comments on making the selected option selected.

First, your HTML generation code would be more robust if rather than splitting the start and end of the HTML tags...

html = ['<select name="%s">' % field_name]
for k,v in options:
    tmp = '<option '
    if k == selected_item:
            tmp += 'selected '
    tmp += 'value="%s">%s</option>' % (k,v)
    html.append(tmp)
html.append('</select>')

... you generated them as atomic wholes:

opt_bits = ('<option %s value="%s">'% (['','selected'][str(v)==selected_item], str(v)) for v in options)
# If running Python2.5, it's better to use: 'selected' if str(v)==selected_item else '' 
html = '<select name=%s>%s</select>' % '\n'.join(opt_bits)

Second, I'm not sure why you double the options list rather than reusing the same variable name, as I did above:

options = [(str(v),str(v)) for v in options]   # ????

This is unnecessary, unless options is really a dict and you intended something like:

[ (k,v) for k,v in options.iteritems() ]

Finally, your code will break if the list items contain quotes. Look in the Python documentation for standard functions for escaping HTML.

Good luck!

bobince