Sadly, not true. Any code inspecting the choices is going to break,
because there is a lot of code expecting it can unpack the choices
out of 2-tuples.
This must be preserved. Perhaps a Choice type could exist, a lot like
url() in our urls.py files, where we can use a tuple or a special type
to additional parameters.
choices = (('apples', 'Apples'),
('oranges', 'Oranges'),
Choice('bananas', 'Bananas', disabled=True),
('grobblefruit', 'Grobblefruit'))
> 2. I don't understand what you mean by "boilerplate" in the API. A
> "choices" field with a disabled choice looks like:
>
> choices = (('apples', 'Apples'),
> ('oranges', 'Oranges'),
> ('bananas', {'label': 'Bananas',
> 'disabled': True}),
> ('grobblefruit', 'Grobblefruit'))
>
> I can't think of a more concise way of clearly representing that 'bananas'
> is disabled while still allowing it to have a label, unless we want to
> change the "choices" data structure a lot more to something like:
>
> choices = (('apples', 'Apples'),
> ('oranges', 'Oranges'),
> ('bananas', 'Bananas', {'disabled': True}),
> ('grobblefruit', 'Grobblefruit'))
>
> Suggestions & other thoughts welcome :)
> Jody
>
> --
> You received this message because you are subscribed to the Google Groups
> "Django developers" group.
> To post to this group, send email to django-d...@googlegroups.com.
> To unsubscribe from this group, send email to
> django-develop...@googlegroups.com.
> For more options, visit this group at
> http://groups.google.com/group/django-developers?hl=en.
>
--
Read my blog! I depend on your acceptance of my opinion! I am interesting!
http://techblog.ironfroggy.com/
Follow me if you're into that sort of thing: http://www.twitter.com/ironfroggy
> 1. Backwards compatibility is already addressed. If the widget is passed aSadly, not true. Any code inspecting the choices is going to break,
> regular "choices" field, the existing behavior is preserved.
because there is a lot of code expecting it can unpack the choices
out of 2-tuples.
This must be preserved. Perhaps a Choice type could exist, a lot like
url() in our urls.py files, where we can use a tuple or a special type
to additional parameters.
Choice('bananas', 'Bananas', disabled=True),
choices = (('apples', 'Apples'),
('oranges', 'Oranges'),
('grobblefruit', 'Grobblefruit'))
The idea was that Choice would implement __iter__ and __getitem__ such
that it would still unpack as a 2-tuple, so current code just thinks
that is what it is.
> How else can we pass data in to the widget such that it is available to
> render_option()? We could add a disabled_choices parameter to the widget's
> __init__ class, but then fields (such has ChoiceField) would need to handle
> the parameter in the same way they now handle choices.
>
> Changing the choices data structure itself seems like the least bad
> alternative, but I'm worried about breaking things that use choices, as you
> mentioned.
>
> Cheers,
> Jody
>
I can't think of a more concise way of clearly representing that 'bananas' is disabled while still allowing it to have a label [...]
I'd say it would be more backwards compatible, and still reasonably concise to just have a separate attribute:disabled_choices = ('bananas',)
The main ideas are:
1. Add a 'disabled_choices' attribute to the widget that takes an
iterable of choices to disable. I've attached a WIP patch to ticket
16149 following this approach. Optionally this could be passed to the
widget by forms.ChoiceField similarly to the way choices is handled
now.
A concern was raised in the ticket (16149) that this is too specific,
and we should also be able to pass arbitrary HTML attributes like
class, style, and id. I don't understand the use case for passing
these things to an <option>, so I don't think this is worthwhile, but
it's something to consider.
2. Extend the "choices" data structure, as suggested by Calvin
Spealman in the discussion.
Thanks,
Jody
def __init__(self, disabled_choices, *args, **kwargs):
self.disabled_choices = disabled_choices
from django.forms import Select
class SelectWidget(Select):
"""
Subclass of Django's select widget that allows disabling options.
"""
def __init__(self, *args, **kwargs):
self._disabled_choices = []
super(SelectWidget, self).__init__(*args, **kwargs)
@property
def disabled_choices(self):
return self._disabled_choices
@disabled_choices.setter
def disabled_choices(self, other):
self._disabled_choices = other
def create_option(self, name, value, label, selected, index, subindex=None, attrs=None):
option_dict = super(SelectWidget, self).create_option(
name, value, label, selected, index, subindex=subindex, attrs=attrs
)
if value in self.disabled_choices:
option_dict['attrs']['disabled'] = 'disabled'
return option_dict
class MyForm(forms.Form):
status = forms.ChoiceField(required=True, widget=SelectWidget, choices=Status.choices())
def __init__(self, request, *args, **kwargs):
if not request.user.is_superuser:
self.fields['status'].widget.disabled_choices = [1, 4]
super().__init__(*args, **kwargs)