Usually I start by inheriting from one of the existing widgets, add a new desired property and then modify a render method. Here's an example for a filterable select widget I implemented. The filtering is done via jquery mobile.
class FilterableSelectWidget(forms.Select):
def __init__(self, attrs=None, choices=()):
super(FilterableSelectWidget, self).__init__(attrs, choices)
# choices can be any iterable, but we may need to render this widget
# multiple times. Thus, collapse it into a list so it can be consumed
# more than once.
self._data_filter = {}
@property
def data_filter(self):
return self._data_filter
@data_filter.setter
def data_filter(self, attr_dict):
self._data_filter.update(attr_dict)
def render_option(self, selected_choices, option_value, option_label):
option_value = force_text(option_value)
if option_value in selected_choices:
selected_html = mark_safe(' selected="selected"')
if not self.allow_multiple_selected:
# Only allow for a single selection.
selected_choices.remove(option_value)
else:
selected_html = ''
# use self.data_filter
filtertext = self.data_filter.get(option_value)
data_filtertext = 'data-filtertext="{filtertext}"'.\
format(filtertext=filtertext) if filtertext else ''
return format_html('<option value="{0}"{1} {3}>{2}</option>',
option_value,
selected_html,
force_text(option_label),
mark_safe(data_filtertext))
Then in the views where I create a form, I'll set the data_filter for the field.
some_form.fields["some_field"] = \
forms.ChoiceField(choices=choices,
widget=FilterableSelectWidget)
some_form.fields["some_field"].widget.data_filter = \
data_filter