Opened 18 months ago

Last modified 3 months ago

#34034 assigned New feature

Adding a class on ChoiceWidget subwidgets is excessively difficult

Reported by: Claude Paroz Owned by: Mariana
Component: Forms Version: dev
Severity: Normal Keywords:
Cc: Triage Stage: Accepted
Has patch: yes Needs documentation: no
Needs tests: yes Patch needs improvement: yes
Easy pickings: no UI/UX: no

Description

Unless someone shows the magical way, adding a class on a subwidget of ChoiceWidget (and not on the widget itself) is too difficult.

Maybe adding option_attrs (either as class attribute or as __init__ parameter) could be a solution.

Change History (11)

comment:1 by Mark Walker, 18 months ago

Could you add an example of the difficulty?

Looking at the Select widget (source), it defines an attribute multiple;

Code highlighting:

    def get_context(self, name, value, attrs):
      context = super().get_context(name, value, attrs)
      if self.allow_multiple_selected:
          context["widget"]["attrs"]["multiple"] = True
      return context

So I'm assuming this then renders with <select multiple>.

If that's the level you want to add a class to, context["widget"]["attrs"]["class"] = "my-select-class" should then render your classes.

Last edited 18 months ago by Mark Walker (previous) (diff)

comment:2 by Claude Paroz, 18 months ago

That means that you have to subclass the Django widget. Then for subwidgets, you have to override create_option, not get_context. Then you have to take care not overwriting any existing class content. So in some code of mine, I had to write:

    def create_option(self, *args, attrs=None, **kwargs):
        attrs = attrs.copy() if attrs else {}
        if 'class' in attrs:
            attrs['class'] += ' form-check-input'
        else:
            attrs.update({'class': 'form-check-input'})
        return super().create_option(*args, attrs=attrs, **kwargs)

and note that the class name is hardcoded, so if you need another class for another part of a form, you have to set the class name as a subclass attribute, and create other widget subclasses. And as that can touch RadioSelect, CheckboxSelectMultiple, etc., you'll have to create subclasses for all of them (if used, of course).

That's what I mean by "excessingly difficult". But it's doable, of course.

comment:3 by Carlton Gibson, 18 months ago

Summary: Adding a class on ChoiceWidget subwidgets is excessingly difficultAdding a class on ChoiceWidget subwidgets is excessively difficult
Triage Stage: UnreviewedAccepted

Hey Claude,

Maybe adding option_attrs

So I'm looking at this in ChoiceWidget:

    def create_option(
        self, name, value, label, selected, index, subindex=None, attrs=None
    ):
        ...
        option_attrs = (
            self.build_attrs(self.attrs, attrs) if self.option_inherits_attrs else {}
        )
        ...

Do you think pulling that into a property (that could then be overridden) would be sufficient? Maybe the else would pull from an __init__ arg. … 🤔

(Implementation TBD)

Seems a reasonable suggestion anyhow. Thanks.

comment:4 by Claude Paroz, 18 months ago

I'm not yet sure about the implementation (didn't find time until now), but basically, we should be able to handle the following use cases:

  • attrs for parent widget, not on subwidgets (already OK with option_inherits_attrs set to False)
  • same attrs for parent widget AND subwidgets (already OK with option_inherits_attrs set to True)
  • attrs for subwidgets but NOT for parent widget (<-- TODO)

Ideally I would like to be able to do that without subclassing, e.g. by Widget(attrs={...}, option_attrs={...}). I guess experiments will tell if it's doable wrt option attrs inheritance.

comment:5 by David Smith, 17 months ago

Seems a reasonable suggestion to me.

One other thought I had was about using templates. A custom template could be provided to option_template_name or "django/forms/widgets/select_option.html" could be overridden. This may help to avoid Widget(attrs={...}, option_attrs={...}) each time this type of widget is used in a project?

comment:6 by Claude Paroz, 17 months ago

The problem with templates is that it's also very difficult to add classes, as they may conflict with other classes already present in attrs. If there was a mean to complement widget/subwidget classes from attrs with custom classes, then templates could be a viable solution.

comment:7 by Kees Hink, 10 months ago

Owner: changed from nobody to Kees Hink
Status: newassigned

comment:8 by Kees Hink, 10 months ago

Has patch: set
Last edited 10 months ago by Natalia Bidart (previous) (diff)

comment:9 by Natalia Bidart, 10 months ago

Needs tests: set
Patch needs improvement: set

comment:10 by Mariusz Felisiak, 6 months ago

Owner: Kees Hink removed
Status: assignednew

comment:11 by Mariana, 3 months ago

Owner: set to Mariana
Status: newassigned
Note: See TracTickets for help on using tickets.
Back to Top