22Useful form fields for use with SQLAlchemy ORM.
33"""
44import operator
5+ from collections import defaultdict
56
67from wtforms import widgets
78from wtforms .fields import SelectFieldBase
@@ -48,6 +49,15 @@ 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+
53+ Specify `get_group` to allow `option` elements to be grouped into `optgroup`
54+ sections. If a string, this is the name of an attribute on the model
55+ containing the group name. If a one-argument callable, this callable will
56+ be passed the model instance and expected to return a group name. Otherwise,
57+ the `option` elements will not be grouped. Note: the result of `get_group`
58+ will be used as both the grouping key and the display label in the `select`
59+ options.
60+
5161 Specify `get_render_kw` to apply HTML attributes to each option. If a
5262 string, this is the name of an attribute on the model containing a
5363 dictionary. If a one-argument callable, this callable will be passed the
@@ -69,6 +79,7 @@ def __init__(
6979 query_factory = None ,
7080 get_pk = None ,
7181 get_label = None ,
82+ get_group = None ,
7283 get_render_kw = None ,
7384 allow_blank = False ,
7485 blank_text = "" ,
@@ -93,6 +104,15 @@ def __init__(
93104 else :
94105 self .get_label = get_label
95106
107+ if get_group is None :
108+ self ._has_groups = False
109+ else :
110+ self ._has_groups = True
111+ if isinstance (get_group , str ):
112+ self .get_group = operator .attrgetter (get_group )
113+ else :
114+ self .get_group = get_group
115+
96116 if get_render_kw is None :
97117 self .get_render_kw = lambda _ : {}
98118 elif isinstance (get_render_kw , str ):
@@ -133,6 +153,26 @@ def iter_choices(self):
133153 for pk , obj in self ._get_object_list ():
134154 yield (pk , self .get_label (obj ), obj == self .data , self .get_render_kw (obj ))
135155
156+ def has_groups (self ):
157+ return self ._has_groups
158+
159+ def iter_groups (self ):
160+ if self .has_groups ():
161+ groups = defaultdict (list )
162+ for pk , obj in self ._get_object_list ():
163+ groups [self .get_group (obj )].append ((pk , obj ))
164+ for group , choices in groups .items ():
165+ yield (group , self ._choices_generator (choices ))
166+
167+ def _choices_generator (self , choices ):
168+ if not choices :
169+ _choices = []
170+ else :
171+ _choices = choices
172+
173+ for pk , obj in _choices :
174+ yield (pk , self .get_label (obj ), obj == self .data , self .get_render_kw (obj ))
175+
136176 def process_formdata (self , valuelist ):
137177 if valuelist :
138178 if self .allow_blank and valuelist [0 ] == "__None" :
0 commit comments