quixote/form widget.py,1.35,1.36
David Binger <dbinger-fVcApmY9cLvQ3/1i3zOLAti2O/[email protected]>
| Newsgroups | gmane.comp.web.quixote.cvs |
|---|---|
| Message-ID | <[email protected]> |
Update of /home/cvs/quixote/form
In directory hewson:/tmp/cvs-serv1099
Modified Files:
widget.py
Log Message:
Changed select widgets to use the description itself as the value
attribute in the tags instead of the index of the description in the
list of descriptions. This addresses the problem that happens when the
submitter's widget has a list of descriptions that is different from
the parsing widget.
Replaced the allowed_values and descriptions attributes on select
widgets with a single 'options' attribute, which holds a list of
(description, object) pairs. Outside code that wants these separate
lists can should use the new get_allowed_values() and
get_descriptions() methods. Also added a set_options() method to
select widgets which may be used like set_allowed_values() if you
already have the list of (description, object) pairs.
Updated docstrings.
Index: widget.py
===================================================================
RCS file: /home/cvs/quixote/form/widget.py,v
retrieving revision 1.35
retrieving revision 1.36
diff -u -d -r1.35 -r1.36
--- widget.py 31 Oct 2002 17:23:30 -0000 1.35
+++ widget.py 1 Nov 2002 14:29:22 -0000 1.36
@@ -36,6 +36,7 @@
Instance attributes:
name : string
+ value : any
Feel free to access these directly; to set them, use the 'set_*()'
modifier methods.
@@ -118,7 +119,7 @@
class StringWidget (Widget):
"""Widget for entering a single string: corresponds to
- "<input type=text>" in HTML.
+ '<input type="text">' in HTML.
Instance attributes:
value : string
@@ -221,14 +222,16 @@
class SelectWidget (Widget):
"""Widget for single or multiple selection; corresponds to
<select name=...>
- <option value=foo>Foo</option>
+ <option value="Foo">Foo</option>
...
</select>
Instance attributes:
- allowed_values : [any]
- descriptions : [string]
+ options : [ (description:htmltext : value:any) ]
+ value : any
+ The value is None or an element of dict(options.values()).
size : int
+ The number of options that should be presented without scrolling.
"""
# NB. 'widget_type' not set here because this is an abstract class: it's
@@ -245,56 +248,75 @@
self.set_value(value)
self.size = size
+ def get_allowed_values (self):
+ return [ object for description, object in self.options ]
+
+ def get_descriptions (self):
+ return [ description for description, object in self.options ]
def set_value (self, value):
- if value in self.allowed_values:
- self.value = value
- else:
- self.value = None
+ self.value = None
+ for description, object in self.options:
+ if value == object:
+ self.value = value
+ break
+ def set_options (self, options, sort=0):
+ """(options: [(description:any, object:any)])
+ Set the options list, applying htmlescape() to each description.
+ Make sure that no descriptions are duplicated.
+ If the sort keyword argument is true, sort
+ the options by case-insensitive lexicographic order of descriptiosn,
+ except that options with value None appear before others.
+ """
+ self.options = [ (htmlescape(description), object)
+ for description, object in options ]
+ found = {}
+ for description, object in self.options:
+ assert description not in found, (
+ "description repeated: %r" % description)
+ found[description] = 1
+ if sort:
+ def compare(a, b):
+ a_description, a_value = a
+ b_description, b_value = b
+ if a_value is None:
+ if b_value is not None:
+ return -1
+ elif b_value is None:
+ return 1
+ return cmp(a_description.lower(), b_description.lower())
+ self.options.sort(compare)
def set_allowed_values (self, allowed_values, descriptions, sort=0):
+ """(allowed_values:[any], descriptions:[any], sort:boolean=0)
+
+ Set the options for this widget. The allowed_values and descriptions
+ parameters must be lists or tuples of the same length.
+ The sort option causes the options to be sorted using case-insensitive
+ lexicographic order of descriptions, except that options with value
+ None appear before others.
+ """
assert type(allowed_values) in (ListType, TupleType), (
- "allowed_values for '%s' not a list or tuple: got %r" %
- (self.name, allowed_values))
- self.allowed_values = allowed_values
+ "allowed_values for '%s' not a list or tuple: got %r" % (
+ self.name, allowed_values))
if descriptions is None:
- self.descriptions = []
- for v in self.allowed_values:
- if v is None:
- v = ""
+ descriptions = []
+ for value in allowed_values:
+ if value is None:
+ descriptions.append(htmltext(""))
else:
- v = str(v)
- self.descriptions.append(v)
+ descriptions.append(htmlescape(value))
else:
assert type(descriptions) in (ListType, TupleType), (
- "descriptions for '%s' not a list or tuple: got %r" %
- (self.name, descriptions))
- assert len(self.allowed_values) == len(descriptions), (
- "allowed_values and descriptions must be the same length: "
- "not %s and %s" % (len(self.allowed_values),
- len(descriptions)))
- self.descriptions = descriptions
- if sort:
- def compare(a, b):
- if a[0] is None:
- return -1
- if b[0] is None:
- return 1
- return cmp(a[0].lower(), b[0].lower())
- pairs = zip(self.descriptions, self.allowed_values)
- pairs.sort(compare)
- self.allowed_values = []
- self.descriptions = []
- for description, value in pairs:
- self.allowed_values.append(value)
- self.descriptions.append(description)
-
+ "descriptions for '%s' not a list or tuple: got %r" % (
+ self.name, descriptions))
+ assert len(descriptions) == len(allowed_values)
+ self.set_options(zip(descriptions, allowed_values), sort=sort)
def is_selected (self, value):
return value == self.value
-
def render (self, request):
if self.widget_type == "multiple_select":
multiple = ValuelessAttr
@@ -307,25 +329,21 @@
tags = [htmltag("select", name=self.name,
multiple=multiple, onchange=onchange,
size=self.size)]
-
- for i in range(len(self.allowed_values)):
- if self.is_selected(self.allowed_values[i]):
+ for description, object in self.options:
+ if self.is_selected(object):
selected = ValuelessAttr
else:
selected = None
r = htmltag("option",
- value=str(i),
+ value=description,
selected=selected)
- tags.append(r + self.descriptions[i] + htmltext('</option>'))
+ tags.append(r + description + htmltext('</option>'))
tags.append(htmltext("</select>"))
return htmltext("\n").join(tags)
class SingleSelectWidget (SelectWidget):
"""Widget for single selection.
-
- Instance attributes:
- value : any
"""
widget_type = "single_select"
@@ -336,13 +354,10 @@
if value:
if type(value) is ListType:
raise FormValueError, "cannot select multiple values"
- try:
- index = int(value)
- except ValueError:
- pass
- else:
- if 0 <= index < len(self.allowed_values):
- self.value = self.allowed_values[index]
+ for description, object in self.options:
+ if value == description:
+ self.value = object
+ break
return self.value
@@ -352,15 +367,6 @@
is returned by the whole group).
Instance attributes:
- value : any
- allowed_values : [any] or (any,)
- the list of possible radiobutton values; in the absence of bugs
- or mischievous clients, 'value' will be one of these.
- descriptions : [string]
- the user-visible radiobutton values: should correspond one-to-one
- to the elements of 'allowed_values', or be None or empty (in
- which case the user-visible values will be the same as the
- behind-the-scenes "real" values)
delim : string = None
string to emit between each radiobutton in the group. If
None, a single newline is emitted.
@@ -375,22 +381,25 @@
self.set_name(name)
self.set_allowed_values(allowed_values, descriptions)
self.set_value(value)
- self.delim = delim or "\n"
+ if delim is None:
+ self.delim = "\n"
+ else:
+ self.delim = delim
def render (self, request):
tags = []
- for i in range(len(self.allowed_values)):
- if self.is_selected(self.allowed_values[i]):
+ for description, object in self.options:
+ if self.is_selected(object):
checked = ValuelessAttr
else:
checked = None
r = htmltag("input",
type="radio",
name=self.name,
- value=str(i),
+ value=description,
checked=checked)
- tags.append(r + self.descriptions[i] + htmltext('</input>'))
- return htmlescape(self.delim).join(tags)
+ tags.append(r + description + htmltext('</input>'))
+ return htmlescape(self.delim).join(tags)
class MultipleSelectWidget (SelectWidget):
@@ -398,48 +407,40 @@
Instance attributes:
value : [any]
+ for multipe selects, the value is None or a list of
+ elements from dict(self.options).values()
"""
widget_type = "multiple_select"
def set_value (self, value):
- if value in self.allowed_values:
- self.value = [value]
+ allowed_values = self.get_allowed_values()
+ if value in allowed_values:
+ self.value = [ value ]
elif type(value) in (ListType, TupleType):
- self.value = [val for val in value
- if val in self.allowed_values] or None
+ self.value = [ element
+ for element in value
+ if element in allowed_values ] or None
else:
self.value = None
def is_selected (self, value):
- if type(self.value) in (ListType, TupleType) and value in self.value:
- return 1
- return value == self.value
-
-
- def append_value (self, value):
- try:
- index = int(value)
- except ValueError:
- pass
+ if self.value is None:
+ return value is None
else:
- if 0 <= index < len(self.allowed_values):
- self.value.append(self.allowed_values[index])
-
+ return value in self.value
def parse (self, request):
value = request.form.get(self.name)
- self.value = []
- if value:
- if type(value) is ListType:
- for val in value:
- self.append_value(val)
- else:
- self.append_value(value)
- if not self.value:
- self.value = None
-
+ if value and type(value) is ListType:
+ self.value = [ object
+ for description, object in self.options
+ if description in value ] or None
+ else:
+ self.value = [ object
+ for description, object in self.options
+ if description == value ] or None
return self.value
@@ -523,12 +524,6 @@
value))
StringWidget.__init__(self, name, value, size, maxlength)
- def set_value (self, value):
- if value is None:
- self.value = None
- else:
- self.value = str(value)
-
def parse (self, request):
value = StringWidget.parse(self, request)
if value:
@@ -586,8 +581,7 @@
if request.form:
SingleSelectWidget.parse(self, request)
if self.value is None:
- self.value = self.allowed_values[0]
-
+ self.value = self.options[0][1]
def render (self, request):
return (SingleSelectWidget.render(self, request) +