How to render DateField with 3 selected

I am looking for the simplest and cleanest way to render a basic DateField with choice 3.

<select>day</select><select>month</select><select>year</select>

      

(and if possible use "format" to choose how the final rendering is displayed)

+3


source to share


3 answers


Final widget: (support for multiple formats, not just 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))

      

+1


source


You can take advantage of the fact that DateField will handle multi-valued inputs and concatenate them together with a space, so you can avoid the secondary form and just provide a sequence of inputs instead:



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())

      

+1


source


I've used a custom widget and it works, but it's not perfect yet. Any other ideas for 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') 

      

0


source







All Articles