26👍
Django’s form widgets offer a way to pass a list of attributes that should be rendered on the <option>
tag:
my_choices = ( ('one', 'One'), ('two', 'Two'))
class MyForm(forms.Form):
some_field = forms.ChoiceField(choices=my_choices,
widget=forms.Select(attrs={'disabled':'disabled'}))
Unfortunately, this won’t work for you because the attribute will be applied to EVERY option tag that is rendered. Django has no way to automatically know which should be enabled and which should be disabled.
In your case, I recommend writing a custom widget. It’s pretty easy to do, and you don’t have that much custom logic to apply. The docs on this are here. In short though:
- subclass
forms.Select
, which is the default select renderer - in your subclass, implement the
render(self, name, value, attrs)
method. Use your custom logic to determine if thevalue
qualifies as needing to be disabled. Have a look at the very short implementation ofrender
indjango/forms/widgets.py
if you need inspriation.
Then, define your form field to use your custom widget:
class MyForm(forms.Form):
some_field = forms.ChoiceField(choices=my_choices,
widget=MyWidget)
13👍
You can create Choices as mentioned by Bryan like this. In the below options Root 1, Root 2 are automatically disabled and they will look like Group Options
CHOICES = (
('-- Root 1--',
(
('ELT1', 'ELT1'),
('ELT2', 'ELT2'),
('ELT3', 'ELT3'),
)
),
('-- Root 2--',
(
('ELT3', 'ELT3'),
('ELT4', 'ELT4'),
)
),
)
The above option will display like this. In the below image, Root 1 and Root 2 are not selectable.
Hope this will clear your problem
-Vikram
- [Django]-Custom django admin templates not working
- [Django]-Is this the right way to do dependency injection in Django?
- [Django]-How to change Django Admin Custom list field label?
8👍
field_choices = (
('','Make choice'),
(1,'first'),
(2,'second'),
(3,'third')
)
from django.forms import Select
class Select(Select):
def create_option(self, *args,**kwargs):
option = super().create_option(*args,**kwargs)
if not option.get('value'):
option['attrs']['disabled'] = 'disabled'
if option.get('value') == 2:
option['attrs']['disabled'] = 'disabled'
return option
- [Django]-How to reset the sequence for IDs on PostgreSQL tables
- [Django]-In a Django QuerySet, how to filter for "not exists" in a many-to-one relationship
- [Django]-CherryPy vs Django
6👍
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=(('on', 'On'), ('off', 'Off')))
def __init__(self, request, *args, **kwargs):
super().__init__(*args, **kwargs)
if not request.user.is_superuser:
self.fields['status'].widget.disabled_choices = ['off']
- [Django]-Django: Model Form "object has no attribute 'cleaned_data'"
- [Django]-How can I get 'pk' or 'id' in `get_context_data` from CBV?
- [Django]-Blank, Null, and Required in Django Models and ModelForms
4👍
It seems django 1.1 will allow “optgroup”: Django documentation
class MyForm(forms.Form):
some_field = forms.ChoiceField(choices=[
('Audio', (
('vinyl', 'Vinyl'),
('cd', 'CD'),
)
),
('Video', (
('vhs', 'VHS Tape'),
('dvd', 'DVD'),
)
),
('unknown', 'Unknown'),
])
This imho is a must have.
- [Django]-How does Django Know the Order to Render Form Fields?
- [Django]-Django and query string parameters
- [Django]-Django RESTful API – django-piston vs. django-tastypie
4👍
Simple questions sometimes have complex answers in Django. I spent a lot of time getting this to work well. Combining Jarrett’s overview with an important note from jnns about render_option
and some help from #django on freenode I have a well-working complete example solution:
First, this example assumes a normally defined choices-type CharField in a model I call Rule. I subclass my own TimeStampedModel
but you can use a models.Model
:
class Rule(TimeStampedModel):
...
# Rule Type
SHORT_TERM_RULE = 'ST'
MAX_SIGHTINGS_PER_PERIOD_RULE = "MA"
WHITE_LIST_RULE = "WL"
BLACK_LIST_RULE = "BL"
RULE_CHOICES = (
(SHORT_TERM_RULE, 'Short Term Rule'),
(MAX_SIGHTINGS_PER_PERIOD_RULE, 'Max Sightings Per Period Rule'),
(WHITE_LIST_RULE, 'White List Rule'),
(BLACK_LIST_RULE, 'Black List Rule'),
)
rule_type = models.CharField(
max_length=2,
choices=RULE_CHOICES,
default=SHORT_TERM_RULE,
)
...
In forms.py, define the widget subclassing Select
that accepts disabled_choices
. It has a custom render_option()
that adds disabled
to the html output of option tags if their choice is included on the passed in disabled_choices
list. Note, I left most of the render_option()
code from Select
as-is:
class MySelect(Select):
def __init__(self, attrs=None, choices=(), disabled_choices=()):
super(MySelect, self).__init__(attrs, choices=choices)
self.disabled_choices = disabled_choices
def render_option(self, selected_choices, option_value, option_label):
if option_value is None:
option_value = ''
option_value = force_text(option_value)
if option_value in selected_choices:
selected_html = mark_safe(' selected="selected"')
if not self.allow_multiple_selected:
selected_choices.remove(option_value)
else:
selected_html = ''
for key, value in self.disabled_choices:
if option_value in key:
return format_html('<option disabled value="{}"{}>{}</option>', option_value, selected_html,
force_text(option_label))
return format_html('<option value="{}"{}>{}</option>', option_value, selected_html, force_text(option_label))
Then, in defining the form subclassing ModelForm
, check for a passed-in disabled_choices
list and initialize the field accordingly. In this example, I also sneak in a default choice.
class RuleChoiceForm(ModelForm):
class Meta:
model = Rule
fields = ['rule_type']
# Add a default choice to the list defined in the Rule model
default_choice = ('DF', 'Choose a Rule Type...')
choices = list(Rule.RULE_CHOICES)
choices.insert(0, default_choice)
choices = tuple(choices)
rule_type = forms.ChoiceField(widget=MySelect(attrs={'class': 'form-control'}, disabled_choices=[]),
choices=choices)
def __init__(self, *args, disabled_choices=None, **kwargs):
super(RuleChoiceForm, self).__init__(*args, **kwargs)
if disabled_choices:
self.fields['rule_type'].widget.disabled_choices = disabled_choices
Then in your view, define disabled_choices
as a list, appending choices from your _CHOICES
var in your model and pass it into the form instantiation. In my logic, I use list comprehension of RULE_CHOICES
to get the tuple of the choice I want to disable. Though there may be a simpler way, please feel free to post suggestions to simplify or improve this answer.
disabled_choices = []
# Logic disabling choices
if Rule.objects.filter(route=route, rule_type=Rule.SHORT_TERM_RULE).exists():
disabled_choices.append([item for item in Rule.RULE_CHOICES if Rule.SHORT_TERM_RULE in item][0])
disabled_choices.append([item for item in Rule.RULE_CHOICES if Rule.MAX_SIGHTINGS_PER_PERIOD_RULE in item][0])
rule_choice_form = RuleChoiceForm(disabled_choices=disabled_choices)
- [Django]-Can I make an admin field not required in Django without creating a form?
- [Django]-Received "ValueError: Found wrong number (0) of constraints for …" during Django migration
- [Django]-How to obtain and/or save the queryset criteria to the DB?
2👍
Are you trying to create a menu in which the list items are separated into categories, and you don’t want the categories themselves to be selectable?
If so, you can achieve this by having your template render the field using tags, e.g.
<select name="my_field" id="id_my_field">
<optgroup label="-- Root 1 entry --">
<option value="1">Elt 1</option>
<option value="2">Elt 2</option>
<option value="3">Elt 3</option>
</optgroup>
<optgroup label="--- Root 2 entry ---">
<option value="4">Elt 4</option>
<option value="5">Elt 5</option>
</optgroup>
</select>
- [Django]-How to return static files passing through a view in django?
- [Django]-ValueError: Related model u'app.model' cannot be resolved
- [Django]-How to completely dump the data for Django-CMS
-1👍
I think it’s better to do this using JavaScript.
Add the following line of code to the HTML page that containts your select element:
<script>
document.querySelector("select").options[0].disabled = true;
</script>
And if you want to disable an option in multiple select elements in your page you can do the following:
<script>
document.querySelectorAll('select').forEach(function(element) {
element.options[0].disabled = true;
});
</script>
- [Django]-Logging requests to django-rest-framework
- [Django]-What is {% block content %} and {% endblock content %} for in Django?
- [Django]-Best way to make Django's login_required the default