Opened 3 years ago
Last modified 12 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: | 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 , 3 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 , 3 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 , 3 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_attrsset toFalse) - same attrs for parent widget AND subwidgets (already OK with
option_inherits_attrsset 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 , 3 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 , 3 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 , 2 years ago
| Owner: | changed from to |
|---|---|
| Status: | new → assigned |
comment:9 by , 2 years ago
| Needs tests: | set |
|---|---|
| Patch needs improvement: | set |
comment:10 by , 2 years ago
| Owner: | removed |
|---|---|
| Status: | assigned → new |
comment:11 by , 22 months ago
| Owner: | set to |
|---|---|
| Status: | new → assigned |
comment:12 by , 17 months ago
| Needs tests: | unset |
|---|---|
| Patch needs improvement: | unset |
comment:13 by , 16 months ago
| Patch needs improvement: | set |
|---|
comment:15 by , 12 months ago
| Patch needs improvement: | set |
|---|
Could you add an example of the difficulty?
Looking at the
Selectwidget (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.