Here's my horribly hacked up copy of Choice that works for what I need. The changes from the normal Choice are that choicesAttribute is really choicesMethod, and that it's supposed to return a list of (id, text), and the ids are used as the keys sent to the client, instead of the offset in the list. Offset in the list is worthless to me, because the list is dynamically generated and won't be consistent. class ChoiceTuple(Typed): """Allow the user to pick from a list of "choices", or a list of choices found by accessing the list in the attribute "choicesAttribute" of the object we are configuring. The elements of the list will be rendered by calling "str". """ def __init__(self, choices=None, choicesAttribute=None, *args, **kw): Typed.__init__(self, *args, **kw) if choices is None: self.choices = () else: self.choices = choices self.choicesAttribute = choicesAttribute def coerce(self, val, binding): """Coerce a value with the help of an object, which is the object we are configuring. """ try: val = int(val) except ValueError: raise InputError("%r is an invalid choice." % val) return val class ChoiceTupleRenderer(webform.BaseInputRenderer): def input(self, context, slot, data, name, value): tv = data.typedValue if tv.choicesAttribute: choices = getattr(context.locate(iformless.IConfigurable).boundTo, tv.choicesAttribute)(context, data) else: choices = tv.choices if isinstance(choices, defer.Deferred): return choices.addCallback(self.input2, context, slot, data, name, value) self.input2(choices, context, slot, data, name, value) def input2(self, choices, context, slot, data, name, value): numChoices = len(choices) if numChoices == 0: return None selecter = tags.select(id=formutils.keyToXMLID(context.key), name=name) for key, val in choices: if key == value: selecter[tags.option(value=str(key), selected="selected")[str(val)]] else: selecter[tags.option(value=str(key))[str(val)]] return slot[selecter]