I am using Django everyday now for three month and it is really great. Fast web application development.
I have still one thing that I cannot do exactly how I want to.
It is the SelectField and SelectMultiple Field.
I want to be able to put some args to an option of a Select.
I finally success with the optgroup :
class EquipmentField(forms.ModelChoiceField):
def __init__(self, queryset, **kwargs):
super(forms.ModelChoiceField, self).__init__(**kwargs)
self.queryset = queryset
self.to_field_name=None
group = None
list = []
self.choices = []
for equipment in queryset:
if not group:
group = equipment.type
if group != equipment.type:
self.choices.append((group.name, list))
group = equipment.type
list = []
else:
list.append((equipment.id, equipment.name))
But for another ModelForm, I have to change the background color of every option, using the color property of the model.
Do you know how I can do that ?
Thank you.
What you need to do, is to change the output which is controlled by the widget. Default is the select widget, so you can subclass it. It looks like this:
class Select(Widget):
def __init__(self, attrs=None, choices=()):
super(Select, self).__init__(attrs)
# choices can be any iterable, but we may need to render this widget
# multiple times. Thus, collapse it into a list so it can be consumed
# more than once.
self.choices = list(choices)
def render(self, name, value, attrs=None, choices=()):
if value is None: value = ''
final_attrs = self.build_attrs(attrs, name=name)
output = [u'<select%s>' % flatatt(final_attrs)]
options = self.render_options(choices, [value])
if options:
output.append(options)
output.append('</select>')
return mark_safe(u'\n'.join(output))
def render_options(self, choices, selected_choices):
def render_option(option_value, option_label):
option_value = force_unicode(option_value)
selected_html = (option_value in selected_choices) and u' selected="selected"' or ''
return u'<option value="%s"%s>%s</option>' % (
escape(option_value), selected_html,
conditional_escape(force_unicode(option_label)))
# Normalize to strings.
selected_choices = set([force_unicode(v) for v in selected_choices])
output = []
for option_value, option_label in chain(self.choices, choices):
if isinstance(option_label, (list, tuple)):
output.append(u'<optgroup label="%s">' % escape(force_unicode(option_value)))
for option in option_label:
output.append(render_option(*option))
output.append(u'</optgroup>')
else:
output.append(render_option(option_value, option_label))
return u'\n'.join(output)
It's a lot of code. But what you need to do, is to make your own widget with an altered render method. It's the render method that determines the html that is created. In this case, it's the render_options method you need to change. Here you could include some check to determine when to add a class, which you could style.
Another thing, in your code above it doesn't look like you append the last group choices. Also you might want to add an order_by() to the queryset, as you need it to be ordered by the type. You could do that in the init method, so you don't have to do it all over when you use the form field.
render_option has been removed from Django 1.11 onwards. This is what I did to achieve this. A little bit of digging and this seems straightforward and neat. Works with Django 2.0+
class CustomSelect(forms.Select):
def __init__(self, attrs=None, choices=()):
self.custom_attrs = {}
super().__init__(attrs, choices)
def create_option(self, name, value, label, selected, index, subindex=None, attrs=None):
index = str(index) if subindex is None else "%s_%s" % (index, subindex)
if attrs is None:
attrs = {}
option_attrs = self.build_attrs(self.attrs, attrs) if self.option_inherits_attrs else {}
if selected:
option_attrs.update(self.checked_attribute)
if 'id' in option_attrs:
option_attrs['id'] = self.id_for_label(option_attrs['id'], index)
# setting the attributes here for the option
if len(self.custom_attrs) > 0:
if value in self.custom_attrs:
custom_attr = self.custom_attrs[value]
for k, v in custom_attr.items():
option_attrs.update({k: v})
return {
'name': name,
'value': value,
'label': label,
'selected': selected,
'index': index,
'attrs': option_attrs,
'type': self.input_type,
'template_name': self.option_template_name,
}
class MyModelChoiceField(ModelChoiceField):
# custom method to label the option field
def label_from_instance(self, obj):
# since the object is accessible here you can set the extra attributes
if hasattr(obj, 'type'):
self.widget.custom_attrs.update({obj.pk: {'type': obj.type}})
return obj.get_display_name()
The form:
class BookingForm(forms.ModelForm):
customer = MyModelChoiceField(required=True,
queryset=Customer.objects.filter(is_active=True).order_by('name'),
widget=CustomSelect(attrs={'class': 'chosen-select'}))
The output which I needed is as:
<select name="customer" class="chosen-select" required="" id="id_customer">
<option value="" selected="">---------</option>
<option value="242" type="CNT">AEC Transcolutions Private Limited</option>
<option value="243" type="CNT">BBC FREIGHT CARRIER</option>
<option value="244" type="CNT">Blue Dart Express Limited</option>
I run into this question many times when searching by
'how to customize/populate Django SelectField options'
The answer provided by Dimitris Kougioumtzis is quite easy
Hope it could help somebody like me.
# forms.py
from django.forms import ModelForm, ChoiceField
from .models import MyChoices
class ProjectForm(ModelForm):
choice = ChoiceField(choices=[
(choice.pk, choice) for choice in MyChoices.objects.all()])
# admin.py
class ProjectAdmin(BaseAdmin):
form = ProjectForm
....
You should not mess with form fields for adding some custom attributes to the rendered html tag. But you should subclass and add a these to the Widget.
From the docs: customizing-widget-instances
You can submit attrs dictionary to the form Widgets, that render as attributes on the output form widgets.
class CommentForm(forms.Form):
name = forms.CharField(
widget=forms.TextInput(attrs={'class':'special'}))
url = forms.URLField()
comment = forms.CharField(
widget=forms.TextInput(attrs={'size':'40'}))
Django will then include the extra attributes in the rendered output:
>>> f = CommentForm(auto_id=False)
>>> f.as_table()
<tr><th>Name:</th><td><input type="text" name="name" class="special"/></td></tr>
<tr><th>Url:</th><td><input type="text" name="url"/></td></tr>
<tr><th>Comment:</th><td><input type="text" name="comment" size="40"/></td></tr>
http://code.djangoproject.com/browser/django/trunk/django/newforms/widgets.py?rev=7083
As seen under the class Select(Widget):, there is no way to add the style attribute to an option tag. To this, you will have to subclass this widget and add such functionality.
The class Select(Widget): definition only adds style attribute to the main select tag.
Related
I have created a custom SelectTimeDateWidget that used the default SelectDateWidget and a custom SelectTimeWidget
class SelectDateTimeWidget(forms.MultiWidget):
supports_microseconds = False
def __init__(self, attrs=None, date_format=None, time_format=None):
widgets = (SelectDateWidget(empty_label=( "Year", "Month", "Day")),
SelectTimeWidget(use_seconds=False))
super(SelectDateTimeWidget, self).__init__(widgets, attrs)
def decompress(self, value):
if value:
value = to_current_timezone(value)
return [value.date(), value.time().replace(microsecond=0)]
return [None, None]
The widgets all work fine but because they are places next to one another in the form template it looks really clustered and not very user friendly. So I need a way to put a break tag and maybe a label tag in between them. I cant find anything on the web(Maybe I'm not wording it right)
This is the code within the form template and both widgets are in that one tag so i'm struggling to think of a way to break them up onto different lines, Any ideas?
<label> Start Time/Date:</label>
{{form.start}},
You can just overwrite format_output method from MultiWidget. It takes one argument, rendered_widgets, which is just a list of rendered sub-widgets. Default implementation just joins them, but you can do with them whatever you want. Example:
def format_output(self, rendered_widgets):
return '<br>'.join(rendered_widgets)
Or, if you want to write something between widgets in template, add this method to your MultiWidget implementation:
def subwidgets(self, name, value, attrs=None):
if self.is_localized:
for widget in self.widgets:
widget.is_localized = self.is_localized
# value is a list of values, each corresponding to a widget
# in self.widgets.
if not isinstance(value, list):
value = self.decompress(value)
output = []
final_attrs = self.build_attrs(attrs)
id_ = final_attrs.get('id')
for i, widget in enumerate(self.widgets):
try:
widget_value = value[i]
except IndexError:
widget_value = None
if id_:
final_attrs = dict(final_attrs, id='%s_%s' % (id_, i))
output.append(widget.render(name + '_%s' % i, widget_value, final_attrs))
return output
And you should be able to use {{ form.start.0 }}, {{ form.start.1 }} and so on in your template.
I just want to ask how we can add additional dynamic html5 data attribute in each fiels in django admin forms.
For example
<option value ="1" data-desc="desc1">hello1</option>
<option value ="2" data-desc="desc2">hello2</option>
Thanks,
Kel
You need to subclass Django's ModelChoiceField and modify it's render_options() and render_option() methods to display the object's attributes you need. I guess you also need your own subclass of ModelChoiceIterator so that it not only spits out id/label tuples but all the data you need.
Example
I just found an implementation of a custom SelectWidget in OpenStack's dashboard:
class SelectWidget(widgets.Select):
"""
Customizable select widget, that allows to render
data-xxx attributes from choices.
.. attribute:: data_attrs
Specifies object properties to serialize as
data-xxx attribute. If passed ('id', ),
this will be rendered as:
<option data-id="123">option_value</option>
where 123 is the value of choice_value.id
.. attribute:: transform
A callable used to render the display value
from the option object.
"""
def __init__(self, attrs=None, choices=(), data_attrs=(), transform=None):
self.data_attrs = data_attrs
self.transform = transform
super(SelectWidget, self).__init__(attrs, choices)
def render_option(self, selected_choices, option_value, option_label):
option_value = force_unicode(option_value)
other_html = (option_value in selected_choices) and \
u' selected="selected"' or ''
if not isinstance(option_label, (basestring, Promise)):
for data_attr in self.data_attrs:
data_value = html.conditional_escape(
force_unicode(getattr(option_label,
data_attr, "")))
other_html += ' data-%s="%s"' % (data_attr, data_value)
if self.transform:
option_label = self.transform(option_label)
return u'<option value="%s"%s>%s</option>' % (
html.escape(option_value), other_html,
html.conditional_escape(force_unicode(option_label)))
EDIT
As long as you provide choices like this, it should work:
def formfield_for_foreignkey(self, db_field, request, **kwargs):
if db_field.name == 'dummy':
widget = SelectWidget(data_attrs=('bar',))
choices = [(foo.id, foo) for foo in Foo.objects.all()]
form_field = forms.ChoiceField(
choices=choices, widget=widget)
return form_field
return super().formfield_for_foreignkey(db_field, request, **kwargs)
You can use the get_form() method to tweak the form that is used by a ModelAdmin.
That way you can change the widgets of the fields in question:
class MyModelAdmin(admin.ModelAdmin):
def get_form(self, request, obj=None, **kwargs):
form = super(MyModelAdmin, self).get_form(request, obj, **kwargs)
form.fields['your_field'].widget.attrs.update({'data-hello': 'world'})
return form
I'm working on something like an online store. I'm making a form in which the customer buys an item, and she can choose how many of these item she would like to buy. But, on every item that she buys she needs to choose what its color would be. So there's a non-constant number of fields: If the customer buys 3 items, she should get 3 <select> boxes for choosing a color, if she buys 7 items, she should get 7 such <select> boxes.
I'll make the HTML form fields appear and disappear using JavaScript. But how do I deal with this on my Django form class? I see that form fields are class attributes, so I don't know how to deal with the fact that some form instance should have 3 color fields and some 7.
Any clue?
Jacob Kaplan-Moss has an extensive writeup on dynamic form fields:
http://jacobian.org/writing/dynamic-form-generation/
Essentially, you add more items to the form's self.fields dictionary during instantiation.
Here's another option: how about a formset?
Since your fields are all the same, that's precisely what formsets are used for.
The django admin uses FormSets + a bit of javascript to add arbitrary length inlines.
class ColorForm(forms.Form):
color = forms.ChoiceField(choices=(('blue', 'Blue'), ('red', 'Red')))
ColorFormSet = formset_factory(ColorForm, extra=0)
# we'll dynamically create the elements, no need for any forms
def myview(request):
if request.method == "POST":
formset = ColorFormSet(request.POST)
for form in formset.forms:
print "You've picked {0}".format(form.cleaned_data['color'])
else:
formset = ColorFormSet()
return render(request, 'template', {'formset': formset}))
JavaScript
<script>
$(function() {
// this is on click event just to demo.
// You would probably run this at page load or quantity change.
$("#generate_forms").click(function() {
// update total form count
quantity = $("[name=quantity]").val();
$("[name=form-TOTAL_FORMS]").val(quantity);
// copy the template and replace prefixes with the correct index
for (i=0;i<quantity;i++) {
// Note: Must use global replace here
html = $("#form_template").clone().html().replace(/__prefix_/g', i);
$("#forms").append(html);
};
})
})
</script>
Template
<form method="post">
{{ formset.management_form }}
<div style="display:none;" id="form_template">
{{ formset.empty_form.as_p }}
</div><!-- stores empty form for javascript -->
<div id="forms"></div><!-- where the generated forms go -->
</form>
<input type="text" name="quantity" value="6" />
<input type="submit" id="generate_forms" value="Generate Forms" />
you can do it like
def __init__(self, n, *args, **kwargs):
super(your_form, self).__init__(*args, **kwargs)
for i in range(0, n):
self.fields["field_name %d" % i] = forms.CharField()
and when you create form instance, you just do
forms = your_form(n)
it's just the basic idea, you can change the code to whatever your want. :D
The way I would do it is the following:
Create an "empty" class that inherits from froms.Form, like this:
class ItemsForm(forms.Form):
pass
Construct a dictionary of forms objects being the actual forms, whose composition would be dependent on the context (e.g. you can import them from an external module). For example:
new_fields = {
'milk' : forms.IntegerField(),
'butter': forms.IntegerField(),
'honey' : forms.IntegerField(),
'eggs' : forms.IntegerField()}
In views, you can use python native "type" function to dynamically generate a Form class with variable number of fields.
DynamicItemsForm = type('DynamicItemsForm', (ItemsForm,), new_fields)
Pass the content to the form and render it in the template:
Form = DynamicItemsForm(content)
context['my_form'] = Form
return render(request, "demo/dynamic.html", context)
The "content" is a dictionary of field values (e.g. even request.POST would do).
You can see my whole example explained here.
Another approach: Rather than breaking the normal field initialization flow, we can override fields with a mixin, return an OrderedDict of dynamic fields in generate_dynamic_fields which will be added whenever its set.
from collections import OrderedDict
class DynamicFormMixin:
_fields: OrderedDict = None
#property
def fields(self):
return self._fields
#fields.setter
def fields(self, value):
self._fields = value
self._fields.update(self.generate_dynamic_fields())
def generate_dynamic_fields(self):
return OrderedDict()
A simple example:
class ExampleForm(DynamicFormMixin, forms.Form):
instance = None
def __init__(self, instance = None, data=None, files=None, auto_id='id_%s', prefix=None, initial=None,
error_class=ErrorList, label_suffix=None, empty_permitted=False, field_order=None,
use_required_attribute=None, renderer=None):
self.instance = instance
super().__init__(data, files, auto_id, prefix, initial, error_class, label_suffix, empty_permitted, field_order,
use_required_attribute, renderer)
def generate_dynamic_fields(self):
dynamic_fields = OrderedDict()
instance = self.instance
dynamic_fields["dynamic_choices"] = forms.ChoiceField(label=_("Number of choices"),
choices=[(str(x), str(x)) for x in range(1, instance.number_of_choices + 1)],
initial=instance.initial_choice)
return dynamic_fields
Updated with my final solution, below.
I wrote a custom Django form widget to create a range query. It renders two input fields to define the min and max values for a query.
With well-crafted forms and widgets, fields can be filled with the values from the last query like so:
form = my_form(request.GET)
However, I cannot figure out a way to fill the values of those fields in my custom widget. Here is the widget code:
class MinMax(Widget):
input_type = None # Subclasses must define this.
def _format_value(self, value):
if self.is_localized:
return formats.localize_input(value)
return value
def render(self, name, value, attrs=None):
if value is None:
value = ''
final_attrs = self.build_attrs(attrs, type=self.input_type, name=name)
if value != '':
# Only add the 'value' attribute if a value is non-empty.
final_attrs['value'] = force_unicode(self._format_value(value))
return mark_safe(u'<input type="text" name="min-%s" /> to
<input type="text" name="max-%s" />' % (name, name) )
Probably because of the custom input field names, the values are not accessible. Is there a way to route them, or a way to rewrite the widget to include this useful functionality? One non-widget solution I can think of is some simple jquery logic, but that's less than optimal.
Here's the code I ended up using:
class MinMax(MultiWidget):
def __init__(self, attrs=None):
""" pass all these parameters to their respective widget constructors..."""
widgets = (forms.TextInput(attrs=attrs), forms.TextInput(attrs=attrs) )
super(MinMax, self).__init__(widgets, attrs)
def decompress(self, value):
return value or ''
def value_from_datadict(self, data, files, name):
value = ''
for key, value in data.items():
value += value
def format_output(self, rendered_widgets):
"""
Given a list of rendered widgets (as strings), it inserts stuff
between them.
Returns a Unicode string representing the HTML for the whole lot.
"""
rendered_widgets.insert(-1, ' to ')
return u''.join(rendered_widgets)
Note that these fields are returned as fieldname_0, fieldname_1 (and so on if you add additional widgets).
I having trouble with a simple question :
How to have some "disabled" field in a dropdown menu generated via a modelForm and choiceFied in the django Framework ?
At the moment, I cannot figure out how to obtain such an output :
-- Root 1 entry -- (disabled)
-- Elt 1 -- (not disabled)
-- Root 2 entry -- (disabled)
Do you have any advice ?
Pierre
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 the value qualifies as needing to be disabled. Have a look at the very short implementation of render in django/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)
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
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
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']
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.
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)
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>