3

I am creating a custom widget for a datetime field:

class MySplitDateTimeWidget(forms.SplitDateTimeWidget):
    def format_output(self, rendered_widgets):
        mytimeid = self.widgets[1].attrs['id']  #####NEED HELP HERE
        temp = "javascript:$('%s').val(new Date().getHours());" % mytimeid
        temp1 = '<a href="%s">Now</a>' % temp
        return mark_safe(u'%s %s<br />%s %s %s' % \
            (_('Date:'), rendered_widgets[0], _('Time:'), rendered_widgets[1],
            temp1
        ))

I need the "id" attribute of the widget, however self.widgets doesn't include the "id" attribute in attrs. It includes every other attribute though. I'm not sure where this attribute comes from?

rsp
  • 811
  • 2
  • 12
  • 32

2 Answers2

1

I was just grappling with the exact same thing; hopefully this is useful for other people. The "id" attr is set via:

  1. Form is asked to render itself
  2. Form iterates through its fields
  3. For each field, the form calls its custom __getitem__() which wraps the field as a BoundField
  4. The BoundField, in the as_widget() method, is what actually sets the "id" attribute (see also the auto_id() method)
  5. The MultiWidget then performs its render() method, which renders each of its child widgets and then joins them with format_output()

So, to answer your question, you want to get the ID in the render() method and not the format_output() method:

class MySplitDateTimeWidget(forms.SplitDateTimeWidget):
    def render(self, name, value, attrs=None):
        widgets_html = super(MySplitDateTimeWidget, self).render(name, value, attrs)

        # attrs['id'] is the ID of the entire widget, append the prefix to chose the sub-widget
        mytimeid = attrs['id'] + '_0' 
        temp = "javascript:$('%s').val(new Date().getHours());" % mytimeid
        temp1 = '<a href="%s">Now</a>' % temp

        return mark_safe(widgets_html + ' ' + temp1)

    def format_output(self, rendered_widgets):
        return mark_safe(u'%s %s<br />%s %s' % (_('Date:'), rendered_widgets[0], _('Time:'), rendered_widgets[1]))
SMX
  • 1,372
  • 15
  • 14
0

Unless you've overwritten it, the ID should be:

id_[name]

So try:

mytimeid = 'id_'+self.widgets[1].attrs['name']
M.javid
  • 6,387
  • 3
  • 41
  • 56
jlmcdonald
  • 13,408
  • 2
  • 54
  • 64
  • The attrs object does not contain 'name' either. These values seem to be passed to the widget at some point but its not clear to me how or from where. – rsp Nov 10 '12 at 00:09
  • Hmm... do you get the same results if you try to subclass MultiWidget instead (which SplitDateTimeWidget is based on)? You'd have to define an __init__ ... something like this: Like this ... class MySplitDateTimeWidget(forms.MultiWidget): def __init__(self, attrs=None): widgets = (forms.widgets.DateInput(attrs=attrs),forms.widgets.TimeInput(attrs=attrs)) super(MySplitDateTimeWidget, self).__init__(widgets,attrs) – jlmcdonald Nov 10 '12 at 08:41
  • If I try that subclass method I get this error: Caught NotImplementedError while rendering: Subclasses must implement this method. I'm not sure exactly what its referring to there? I spent more time looking through the django and admin source and i'm not sure there's a way to get the id from the format_output method. The django admin uses some client-side javascript to add this type of function to the widget after teh page is loaded. – rsp Nov 12 '12 at 01:17