djangodjango-widgetdjango-multiwidget

How do I use Django's MultiWidget?


The documentation is a bit lacking with respect to this feature.

from django import forms

class TwoInputWidget(forms.MultiWidget):
    """An example widget which concatenates two text inputs with a space"""
    def __init__(self, attrs=None):
        widgets = [forms.TextInput, forms.TextInput]

I can see I need to create a "widgets" property with a list of other widgets, but after that it gets a little Sherlock Holmes.

Would someone please explain to me how to use the MultiWidget widget?


Solution

  • Interesting question and I think perhaps deserving of a little more attention in the docs.

    Here's an example from a question I've just asked:

    class DateSelectorWidget(widgets.MultiWidget):
        def __init__(self, attrs=None, dt=None, mode=0):  
            if dt is not None:
                self.datepos = dt
            else:
                self.datepos = date.today()    
    
            # bits of python to create days, months, years
            # example below, the rest snipped for neatness.
    
            years = [(year, year) for year in year_digits]
    
            _widgets = (
                widgets.Select(attrs=attrs, choices=days), 
                widgets.Select(attrs=attrs, choices=months),
                widgets.Select(attrs=attrs, choices=years),
                )
            super(DateSelectorWidget, self).__init__(_widgets, attrs)
    
        def decompress(self, value):
            if value:
                return [value.day, value.month, value.year]
            return [None, None, None]
    
        def format_output(self, rendered_widgets):
            return u''.join(rendered_widgets)
    

    What've I done?

    Things I haven't, but could have, overriden:

    Example, django markitup's render method:

    def render(self, name, value, attrs=None):
        html = super(MarkItUpWidget, self).render(name, value, attrs)
    
        if self.auto_preview:
            auto_preview = "$('a[title=\"Preview\"]').trigger('mouseup');"
        else: auto_preview = ''
    
        html += ('<script type="text/javascript">'
                '(function($) { '
                 '$(document).ready(function() {'
                 '  $("#%(id)s").markItUp(mySettings);'
                 '  %(auto_preview)s '
                 '});'
                 '})(jQuery);'
                 '</script>' % {'id': attrs['id'],
                                'auto_preview': auto_preview })
        return mark_safe(html)
    

    For example, markitup's django widget does this:

    def _media(self):
            return forms.Media(
                css= {'screen': (posixpath.join(self.miu_skin, 'style.css'),
                                 posixpath.join(self.miu_set, 'style.css'))},
                js=(settings.JQUERY_URL,
                    absolute_url('markitup/jquery.markitup.js'),
                    posixpath.join(self.miu_set, 'set.js')))
        media = property(_media)
    

    Again, it is creating a tuple of paths to the correct location, just as my widget has created a tuple of widgets in the __init__ method.

    I think that covers it for important parts of the MultiWidget class. What you are trying to do does depend on what you've created/which widgets you're using, which is why I can't go into details easily. However, if you want to see the base class for yourself and take a look at the comments, take a look at the source.