[Fixed]-Overriding django's CheckboxSelectMultiple widget for Awesome Bootstrap Checkboxes

1👍

Here is django’s default code in execution order or class hierarchy:

class CheckboxSelectMultiple(RendererMixin, SelectMultiple):
    renderer = CheckboxFieldRenderer            # contains all layout logic
    _empty_value = []


class CheckboxFieldRenderer(ChoiceFieldRenderer): # parent has outer layout
    choice_input_class = CheckboxChoiceInput    #  contains inner layout


class ChoiceFieldRenderer(object):              # outer layout: ul and li
    outer_html = '<ul{id_attr}>{content}</ul>'
    inner_html = '<li>{choice_value}{sub_widgets}</li>'

    def render(self):       # this generates inner layout, and wraps with outer
        ...

class CheckboxChoiceInput(ChoiceInput):
    input_type = 'checkbox'
    ...


class ChoiceInput(SubWidget):                   # inner layout: label and input 
    ...
    def render(self, name=None, value=None, attrs=None, choices=()):
        if self.id_for_label:
            label_for = format_html(' for="{}"', self.id_for_label)
        else:
            label_for = ''
        attrs = dict(self.attrs, **attrs) if attrs else self.attrs
        return format_html(
            '<label{}>{} {}</label>', label_for, self.tag(attrs), self.choice_label
        )
    ...

And now we override necessary parts in reverse:

class AwesomeChoiceInput(ChoiceInput):
    def render(self, name=None, value=None, attrs=None, choices=()):
        if self.id_for_label:
            label_for = format_html(' for="{}"', self.id_for_label)
        else:
            label_for = ''
        attrs = dict(self.attrs, **attrs) if attrs else self.attrs
        return format_html(
            '{}\n<label{}> {}</label>', self.tag(attrs), label_for, self.choice_label   # updated!
        )


class AwesomeChoiceFieldRenderer(ChoiceFieldRenderer):
    outer_html = '<div{id_attr}>{content}</div>'                                            # updated!!
    inner_html = '<div class="checkbox checkbox-primary">{choice_value}{sub_widgets}</div>' # updated!!
    def __init__(self, name, value, attrs, choices):
        super().__init__(name, value, attrs, choices)
        if 'awesome-class' in attrs:
            self.inner_html = '<div class="checkbox ' + attrs.pop('awesome-class') + '">{choice_value}{sub_widgets}</div>'

class AwesomeCheckboxChoiceInput(AwesomeChoiceInput, CheckboxChoiceInput): # this was little tricky
    # there might be better ways of extending a class just to override its parent
    # summary: class A(B) => class extendedA(extendedB, A) # to get whatever was in A also
    pass


class AwesomeCheckboxFieldRenderer(AwesomeChoiceFieldRenderer):  # rest-just connect the pipes
    choice_input_class = AwesomeCheckboxChoiceInput              # as you took off :)                             


class AwesomeCheckboxSelectMultiple(CheckboxSelectMultiple):
    renderer = AwesomeCheckboxFieldRendereraa
👤mehmet

Leave a comment