I'm looking for the simplest and cleanest way to render a basic DateField with 3 select.
<select>day</select><select>month</select><select>year</select>
(and if possible use "format" to choose how to display the final render)
I'm looking for the simplest and cleanest way to render a basic DateField with 3 select.
<select>day</select><select>month</select><select>year</select>
(and if possible use "format" to choose how to display the final render)
You can take advantage of the fact that DateField will handle multiple-valued inputs and join them together with a space, so you can avoid a secondary form and instead just provide a sequence of inputs:
from wtforms.widgets.core import Select, HTMLString, html_params
class SelectDateWidget(object):
FORMAT_CHOICES = {
'%d': [(x, str(x)) for x in range(1, 32)],
'%m': [(x, str(x)) for x in range(1, 13)],
'%y': [(x, str(x)) for x in range(1950, 2014)],
}
def __call__(self, field, **kwargs):
field_id = kwargs.pop('id', field.id)
html = []
for format in field.format.split():
choices = self.FORMAT_CHOICES[format]
id_suffix = format.replace('%', '-')
params = dict(kwargs, name=field.name, id=field_id + id_suffix)
html.append('<select %s>' % html_params(params))
if field.data:
current_value = int(field.data.strftime(format))
else:
current_value = None
for value, label in choices:
selected = (value == current_value)
html.append(Select.render_option(value, label, selected))
html.append('</select>')
return HTMLString(''.join(html))
# Usage
class MyForm(Form):
american_date = DateField(format='%m %d %y', widget=SelectDateWidget())
european_date = DateField(format='%d %m %y', widget=SelectDateWidget())
Final widget: (Support multiple format not only spaces)
class SelectDateWidget(object):
FORMAT_CHOICES = {
'%d': [(x, str(x)) for x in range(1, 32)],
'%m': [(x, str(x)) for x in range(1, 13)]
}
FORMAT_CLASSES = {
'%d': 'select_date_day',
'%m': 'select_date_month',
'%Y': 'select_date_year'
}
def __init__(self, years=range(1930, 2014)):
super(SelectDateWidget, self).__init__()
self.FORMAT_CHOICES['%Y'] = [(x, str(x)) for x in years]
def __call__(self, field, **kwargs):
field_id = kwargs.pop('id', field.id)
html = []
allowed_format = ['%d', '%m', '%Y']
for format in field.format.split():
if (format in allowed_format):
choices = self.FORMAT_CHOICES[format]
id_suffix = format.replace('%', '-')
id_current = field_id + id_suffix
kwargs['class'] = self.FORMAT_CLASSES[format]
try: del kwargs['placeholder']
except: pass
html.append('<select %s>' % html_params(name=field.name, id=id_current, **kwargs))
if field.data:
current_value = int(field.data.strftime(format))
else:
current_value = None
for value, label in choices:
selected = (value == current_value)
html.append(Select.render_option(value, label, selected))
html.append('</select>')
else:
html.append(format)
html.append('<input type="hidden" value="'+format+'" %s></input>' % html_params(name=field.name, id=id_current, **kwargs))
html.append(' ')
return HTMLString(''.join(html))
I used a custom widget and it's working but it's not perfect yet. Other ideas to do this?
class SelectDateWidget(object):
class SelectDateForm(Form):
days = [(x,x) for x in range(1,32)]
months = [(x,x) for x in range(1,13)]
years = [(x,x) for x in range(1950,2014)]
days_select = SelectField(choices=days)
month_select = SelectField(choices=months)
year_select = SelectField(choices=years)
def __call__(self, field, **kwargs):
kwargs.setdefault('id', field.id)
date_form = self.SelectDateForm(prefix=field.name)
days_html = date_form.days_select(class_="input-mini").__html__()
month_html = date_form.month_select(class_="input-mini").__html__()
year_html = date_form.year_select(class_="input-small").__html__()
widget_html = field.format
widget_html = widget_html.replace('%d', days_html)
widget_html = widget_html.replace('%m', month_html)
widget_html = widget_html.replace('%Y', year_html)
return HTMLString(widget_html)
class SelectDateField(DateField):
widget = SelectDateWidget()
def __init__(self, label=None, validators=None, **kwargs):
super(SelectDateField, self).__init__(label, validators, **kwargs)
def pre_validate(self, extra):
form = SelectDateWidget.SelectDateForm(prefix=self.name)
try:
date = datetime.datetime.strptime(form.days_select.data+'-'+form.month_select.data+'-'+form.year_select.data, '%d-%m-%Y')
self.data = date
except:
raise StopValidation(gettext(u'Invalid date.'))
class MyForm(Form):
date = SelectDateField(u'Date', validators=[Required(message=_(u'This field is required.'))], format='%d / %m / %Y')