Opened 2 years ago
Last modified 2 weeks 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: | no | 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 (15)
comment:2 by , 2 years 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 , 2 years ago
Summary: | Adding a class on ChoiceWidget subwidgets is excessingly difficult → Adding a class on ChoiceWidget subwidgets is excessively difficult |
---|---|
Triage Stage: | Unreviewed → Accepted |
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 , 2 years 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 toFalse
) - same attrs for parent widget AND subwidgets (already OK with
option_inherits_attrs
set toTrue
) - 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 , 2 years 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 , 2 years 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 , 18 months ago
Owner: | changed from | to
---|---|
Status: | new → assigned |
comment:9 by , 18 months ago
Needs tests: | set |
---|---|
Patch needs improvement: | set |
comment:10 by , 14 months ago
Owner: | removed |
---|---|
Status: | assigned → new |
comment:11 by , 11 months ago
Owner: | set to |
---|---|
Status: | new → assigned |
comment:12 by , 5 months ago
Needs tests: | unset |
---|---|
Patch needs improvement: | unset |
comment:13 by , 4 months ago
Patch needs improvement: | set |
---|
comment:15 by , 2 weeks ago
Patch needs improvement: | set |
---|
Could you add an example of the difficulty?
Looking at the
Select
widget (source), it defines an attributemultiple
;Code highlighting:
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.