Allow disabling choices in a <select>

classic Classic list List threaded Threaded
10 messages Options
Reply | Threaded
Open this post in threaded view
|

Allow disabling choices in a <select>

Jody McIntyre-4
We need to be able to disable choices in a <select>, which is done by setting the disabled attribute on the <option> tag, for example:
<option value="bananas" disabled="disabled">Bananas</option>

 Currently we're doing this by subclassing the Select widget: http://djangosnippets.org/snippets/2453/

It would be nice if the built in Select widget supported this.  One way would be to replace the existing render_option method with what I've written, and I can prepare a patch if desired, but this approach changes the format of the "choices" data structure to something that won't be understood by other widgets.  Perhaps these widgets should be improved too, but I don't want to do this unless my changes have a good chance of being accepted.

I logged this as a ticket (16149) and was told to discuss it here.  To address aagustin's comments:

1. Backwards compatibility is already addressed.  If the widget is passed a regular "choices" field, the existing behavior is preserved.

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 [hidden email].
To unsubscribe from this group, send email to [hidden email].
For more options, visit this group at http://groups.google.com/group/django-developers?hl=en.
Reply | Threaded
Open this post in threaded view
|

Re: Allow disabling choices in a <select>

Calvin Spealman
On Fri, Jun 3, 2011 at 12:50 PM, Jody McIntyre <[hidden email]> wrote:

> We need to be able to disable choices in a <select>, which is done by
> setting the disabled attribute on the <option> tag, for example:
> <option value="bananas" disabled="disabled">Bananas</option>
>
>  Currently we're doing this by subclassing the Select widget:
> http://djangosnippets.org/snippets/2453/
>
> It would be nice if the built in Select widget supported this.  One way
> would be to replace the existing render_option method with what I've
> written, and I can prepare a patch if desired, but this approach changes the
> format of the "choices" data structure to something that won't be understood
> by other widgets.  Perhaps these widgets should be improved too, but I don't
> want to do this unless my changes have a good chance of being accepted.
>
> I logged this as a ticket (16149) and was told to discuss it here.  To
> address aagustin's comments:
>
> 1. Backwards compatibility is already addressed.  If the widget is passed a
> regular "choices" field, the existing behavior is preserved.

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 [hidden email].
> To unsubscribe from this group, send email to
> [hidden email].
> 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

--
You received this message because you are subscribed to the Google Groups "Django developers" group.
To post to this group, send email to [hidden email].
To unsubscribe from this group, send email to [hidden email].
For more options, visit this group at http://groups.google.com/group/django-developers?hl=en.

Reply | Threaded
Open this post in threaded view
|

Re: Allow disabling choices in a <select>

Jody McIntyre-4
On Fri, Jun 3, 2011 at 1:09 PM, Calvin Spealman <[hidden email]> wrote:
> 1. Backwards compatibility is already addressed.  If the widget is passed a
> regular "choices" field, the existing behavior is preserved.

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.

OK, you're right.  I was only considering widgets but there are lots of other things that use choices.

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'))

url() in urls.py only works because it's an argument to patterns(), which understands how to deal with both tuples and RegexURLPattern objects returned by url().  I don't see how to apply this design to choices without changing everything that uses choices, which is what we're trying to avoid.

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

--
You received this message because you are subscribed to the Google Groups "Django developers" group.
To post to this group, send email to [hidden email].
To unsubscribe from this group, send email to [hidden email].
For more options, visit this group at http://groups.google.com/group/django-developers?hl=en.
Reply | Threaded
Open this post in threaded view
|

Re: Allow disabling choices in a <select>

Calvin Spealman
On Fri, Jun 3, 2011 at 3:00 PM, Jody McIntyre <[hidden email]> wrote:

> On Fri, Jun 3, 2011 at 1:09 PM, Calvin Spealman <[hidden email]>
> wrote:
>>
>> > 1. Backwards compatibility is already addressed.  If the widget is
>> > passed a
>> > regular "choices" field, the existing behavior is preserved.
>>
>> 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.
>
> OK, you're right.  I was only considering widgets but there are lots of
> other things that use choices.
>
>> 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'))
>
> url() in urls.py only works because it's an argument to patterns(), which
> understands how to deal with both tuples and RegexURLPattern objects
> returned by url().  I don't see how to apply this design to choices without
> changing everything that uses choices, which is what we're trying to avoid.

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
>
> --
> You received this message because you are subscribed to the Google Groups
> "Django developers" group.
> To post to this group, send email to [hidden email].
> To unsubscribe from this group, send email to
> [hidden email].
> 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

--
You received this message because you are subscribed to the Google Groups "Django developers" group.
To post to this group, send email to [hidden email].
To unsubscribe from this group, send email to [hidden email].
For more options, visit this group at http://groups.google.com/group/django-developers?hl=en.

Reply | Threaded
Open this post in threaded view
|

Re: Allow disabling choices in a <select>

SmileyChris
In reply to this post by Jody McIntyre-4


On Saturday, June 4, 2011 4:50:12 AM UTC+12, Jody McIntyre wrote:
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',)

--
You received this message because you are subscribed to the Google Groups "Django developers" group.
To view this discussion on the web visit https://groups.google.com/d/msg/django-developers/-/NlVzSVQwYnU5Z3dK.
To post to this group, send email to [hidden email].
To unsubscribe from this group, send email to [hidden email].
For more options, visit this group at http://groups.google.com/group/django-developers?hl=en.
Reply | Threaded
Open this post in threaded view
|

Re: Allow disabling choices in a <select>

Jody McIntyre-4
On Mon, Jun 6, 2011 at 7:49 PM, Chris Beaven <[hidden email]> wrote:

I'd say it would be more backwards compatible, and still reasonably concise to just have a separate attribute:

disabled_choices = ('bananas',)

OK.  I was trying to avoid adding attributes to the widget, but it's definitely backwards compatible.

I've attached a patch to ticket 16149:
https://code.djangoproject.com/attachment/ticket/16149/select_with_disabled.diff

Do people generally agree with this approach?  I will continue work if so.  Still todo: adding similar functionality to other widgets where it makes sense and adding tests.  I could also add disabled_choices support to forms.Choicefield, similarly to the way choices is handled now.  Let me know if you think this is useful.

Thanks,
Jody

--
You received this message because you are subscribed to the Google Groups "Django developers" group.
To post to this group, send email to [hidden email].
To unsubscribe from this group, send email to [hidden email].
For more options, visit this group at http://groups.google.com/group/django-developers?hl=en.
Reply | Threaded
Open this post in threaded view
|

Odp: Re: Allow disabling choices in a <select>

Mateusz Harasymczuk-2
Indeed it is useful, I was using a custom made app to do so.
Thank you

--
Matt Harasymczuk

--
You received this message because you are subscribed to the Google Groups "Django developers" group.
To view this discussion on the web visit https://groups.google.com/d/msg/django-developers/-/M3Y5dFA1Q05ZdFVK.
To post to this group, send email to [hidden email].
To unsubscribe from this group, send email to [hidden email].
For more options, visit this group at http://groups.google.com/group/django-developers?hl=en.
Reply | Threaded
Open this post in threaded view
|

Re: Allow disabling choices in a <select>

Jody McIntyre-4
In reply to this post by SmileyChris
Can a core developer please advise on the preferred design here?

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

--
You received this message because you are subscribed to the Google Groups "Django developers" group.
To post to this group, send email to [hidden email].
To unsubscribe from this group, send email to [hidden email].
For more options, visit this group at http://groups.google.com/group/django-developers?hl=en.

Reply | Threaded
Open this post in threaded view
|

Re: Allow disabling choices in a <select>

Kit Sunde
It's a bit unexpected that disabled_choices isa frozenset in the patch 
when choices is a list. Since disabled_choices is a subset of choices, 
it should be whatever choices is, and it's somewhat common to 
change choices after init it should be expected that disabled_choices 
also change.

On Friday, June 10, 2011 10:10:29 PM UTC+8, Jody McIntyre wrote:
Can a core developer please advise on the preferred design here?

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

--
You received this message because you are subscribed to the Google Groups "Django developers" group.
To post to this group, send email to [hidden email].
To unsubscribe from this group, send email to [hidden email].
Visit this group at http://groups.google.com/group/django-developers?hl=en.
For more options, visit https://groups.google.com/groups/opt_out.
 
 
Reply | Threaded
Open this post in threaded view
|

Re: Allow disabling choices in a <select>

jackotonye
In reply to this post by Jody McIntyre-4
This might be a late answer but this is a simplified version that can be modified using the form instance.

You can either pass a list of values to be disabled i.e

def __init__(self, disabled_choices, *args, **kwargs):
   
self.disabled_choices = disabled_choices

OR
   
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


To disabled an option based on a condition i.e user isn't a superuser.
   
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)


On Friday, June 3, 2011 at 12:50:12 PM UTC-4, Jody McIntyre wrote:
We need to be able to disable choices in a <select>, which is done by setting the disabled attribute on the <option> tag, for example:
<option value="bananas" disabled="disabled">Bananas</option>

 Currently we're doing this by subclassing the Select widget: <a href="http://djangosnippets.org/snippets/2453/" target="_blank" rel="nofollow" onmousedown="this.href=&#39;http://www.google.com/url?q\x3dhttp%3A%2F%2Fdjangosnippets.org%2Fsnippets%2F2453%2F\x26sa\x3dD\x26sntz\x3d1\x26usg\x3dAFQjCNHtDqxteQHEsCswEGfNAS2Qk8ZwNw&#39;;return true;" onclick="this.href=&#39;http://www.google.com/url?q\x3dhttp%3A%2F%2Fdjangosnippets.org%2Fsnippets%2F2453%2F\x26sa\x3dD\x26sntz\x3d1\x26usg\x3dAFQjCNHtDqxteQHEsCswEGfNAS2Qk8ZwNw&#39;;return true;">http://djangosnippets.org/snippets/2453/

It would be nice if the built in Select widget supported this.  One way would be to replace the existing render_option method with what I've written, and I can prepare a patch if desired, but this approach changes the format of the "choices" data structure to something that won't be understood by other widgets.  Perhaps these widgets should be improved too, but I don't want to do this unless my changes have a good chance of being accepted.

I logged this as a ticket (16149) and was told to discuss it here.  To address aagustin's comments:

1. Backwards compatibility is already addressed.  If the widget is passed a regular "choices" field, the existing behavior is preserved.

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 (Contributions to Django itself)" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email].
To post to this group, send email to [hidden email].
Visit this group at https://groups.google.com/group/django-developers.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-developers/bfb05112-e288-48c9-8daa-dbb9ece149d7%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.