3

the basic Django CheckboxSelectMultiple widget allows for a label and value to be passed through to the template. I want to add 2 additional fields from a model but I cannot work out how, although I believe it is through subclassing get_context

I have this model and I would like to include the icon and description in the widget

class AddOnItem(models.Model):
    name = models.CharField(
        verbose_name = 'AddOn Title',
        max_length = 50
    )
    description = models.CharField(
        verbose_name = 'Description',
        max_length = 255
    )
    icon = models.FileField(upload_to="addon_icons/", blank=True, null=True)
    active = models.BooleanField(default=False)

Within my form I've specified this widget

class JobPostWizardForm1(forms.ModelForm):
    class Meta:
        model = Job
        fields = [
            ...,
            'addons'
            ]

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        ...

        self.fields['addons'].widget = AddOnCheckboxSelectMultiple()
        self.fields['addons'].queryset = AddOnItem.objects.filter(active=True)

And I've subclassed the CheckboxSelectMultiple widget as

class AddOnCheckboxSelectMultiple(forms.CheckboxSelectMultiple):
    template_name = 'jobboard/widgets/addon_checkbox_select.html'
    option_template_name = 'jobboard/widgets/addon_checkbox_option.html'

    def get_context(self, name, value, attrs):
        context = super().get_context(name, value,attrs)
        return context

Obviously this doesn't do anything at the moment but I want to add something like

context['icon'] = obj.icon

but I cannot work out how to do this. That is, I do not follow how Django widgets are getting the object.

I would greatly appreciate any help available - thanks

HenryM
  • 5,557
  • 7
  • 49
  • 105

3 Answers3

1

This might not be the best solution, but it works.

I override create_option like so:

def create_option(self, name, value, label, selected, index, subindex=None, attrs=None):
    ctx = super().create_option(name, value, label, selected, index, subindex=subindex, attrs=attrs)
    obj = AddOnItem.objects.get(id=int(value))
    ctx['icon'] = obj.icon
    ctx['description'] = obj.description
    ctx['price'] = obj.price

    return ctx

I can then get these attributes with {{ widget.field_name }} within the template.

bignose
  • 30,281
  • 14
  • 77
  • 110
HenryM
  • 5,557
  • 7
  • 49
  • 105
1

In case of MultiWidget, similarly to @HenryM's answer, one can subclass get_context method and assign values like so:

def get_context(self, name, value, attrs):
    context = super().get_context(name, value, attrs)
    context["some_field"] = "some_value"
    return context

Which in template can be accessed by {{ some_field }}

barbsan
  • 3,418
  • 11
  • 21
  • 28
BaconBad
  • 83
  • 1
  • 9
0

If you have a ModelForm that has a ForeignKey (ModelChoiceField) you can set additional context in custom widget like so:

class CustomRadioSelect(forms.RadioSelect):
    option_template_name = "widgets/test.html"

    def get_context(self, name, value, attrs):
        context = super().get_context(name, value, attrs)
        for option in context['widget']['optgroups']:
            _, opts, _ = option
            for opt in opts:
                opt['other_attributes'] = opt['value'].instance.other_attributes
        return context

In this example my model has an attribute other_attributes that I wanted to access in my widget.

Then in my option_template:

{{ widget.other_attributes }}

This saves another trip to the database to add more information from the model.

Ashley
  • 2,256
  • 1
  • 33
  • 62