class ChoiceWithOtherRenderer(forms.RadioSelect.renderer): def __init__(self, *args, **kwargs): super(ChoiceWithOtherRenderer, self).__init__(*args, **kwargs) def __iter__(self): for input in self.choices: id = '%s_%s' % (self.attrs['id'], input[0]) if 'id' in self.attrs else '' label_for = ' for="%s"' % id if id else '' checked = '' if not force_unicode(input[0]) == self.value else 'checked="true" ' html = ' %s %%s' % (label_for, id, input[0], self.name, checked, input[1]) yield html class ChoiceWithOtherWidget(forms.MultiWidget): def __init__(self, choices): widgets = [forms.RadioSelect(choices=choices, renderer=ChoiceWithOtherRenderer),] for choice in choices: widgets.append(choice[2]) super(ChoiceWithOtherWidget, self).__init__(widgets) def decompress(self, value): if not value: return [None, None] return value def format_output(self, rendered_widgets): render = [] for widget in rendered_widgets[1:]: render.append(widget) render = tuple(render) return rendered_widgets[0] % render class ChoiceWithOtherField(forms.MultiValueField): def __init__(self, *args, **kwargs): fields = [ forms.ChoiceField(widget=forms.RadioSelect(renderer=ChoiceWithOtherRenderer), *args, **kwargs), ] widget = ChoiceWithOtherWidget(choices=kwargs['choices']) kwargs.pop('choices') self._was_required = kwargs.pop('required', True) kwargs['required'] = False super(ChoiceWithOtherField, self).__init__(widget=widget, fields=fields, *args, **kwargs) def compress(self, value): if self._was_required and not value or value[0] in (None, ''): raise forms.ValidationError(self.error_messages['required']) if not value: return [None, u''] return (value[0], value[1] if force_unicode(value[0]) == force_unicode(self.fields[0].choices[-1][0]) else u'')