Skip to content

Commit d78a124

Browse files
author
Jonathan Nagy
committed
Add optgroup support (#43)
1 parent 883350e commit d78a124

File tree

1 file changed

+41
-0
lines changed

1 file changed

+41
-0
lines changed

wtforms_sqlalchemy/fields.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
Useful form fields for use with SQLAlchemy ORM.
33
"""
44
import operator
5+
from collections import defaultdict
56

67
from wtforms import widgets
78
from wtforms.fields import SelectFieldBase
@@ -48,6 +49,14 @@ class QuerySelectField(SelectFieldBase):
4849
model instance and expected to return the label text. Otherwise, the model
4950
object's `__str__` will be used.
5051
52+
Specify `get_group` to allow `option` elements to be grouped into `optgroup`
53+
sections. If a string, this is the name of an attribute on the model
54+
containing the group name. If a one-argument callable, this callable will
55+
be passed the model instance and expected to return a group name. Otherwise,
56+
the `option` elements will not be grouped. Note: the result of `get_group`
57+
will be used as both the grouping key and the display label in the `select`
58+
options.
59+
5160
If `allow_blank` is set to `True`, then a blank choice will be added to the
5261
top of the list. Selecting this choice will result in the `data` property
5362
being `None`. The label for this blank choice can be set by specifying the
@@ -63,6 +72,7 @@ def __init__(
6372
query_factory=None,
6473
get_pk=None,
6574
get_label=None,
75+
get_group=None,
6676
allow_blank=False,
6777
blank_text="",
6878
**kwargs
@@ -86,6 +96,15 @@ def __init__(
8696
else:
8797
self.get_label = get_label
8898

99+
if get_group is None:
100+
self._has_groups = False
101+
else:
102+
self._has_groups = True
103+
if isinstance(get_group, str):
104+
self.get_group = operator.attrgetter(get_group)
105+
else:
106+
self.get_group = get_group
107+
89108
self.allow_blank = allow_blank
90109
self.blank_text = blank_text
91110
self.query = None
@@ -119,6 +138,28 @@ def iter_choices(self):
119138
for pk, obj in self._get_object_list():
120139
yield (pk, self.get_label(obj), obj == self.data)
121140

141+
def has_groups(self):
142+
return self._has_groups
143+
144+
def iter_groups(self):
145+
if self.has_groups():
146+
groups = defaultdict(list)
147+
for pk, obj in self._get_object_list():
148+
groups[self.get_group(obj)].append((pk, obj))
149+
for group, choices in groups.items():
150+
yield (group, self._choices_generator(choices))
151+
152+
def _choices_generator(self, choices):
153+
if not choices:
154+
_choices = []
155+
else:
156+
_choices = choices
157+
158+
for pk, obj in _choices:
159+
yield (pk, self.get_label(obj), obj == self.data, self.get_render_kw(obj))
160+
161+
162+
122163
def process_formdata(self, valuelist):
123164
if valuelist:
124165
if self.allow_blank and valuelist[0] == "__None":

0 commit comments

Comments
 (0)