#
# Example of usage in a form:
#
#priority=ModelChoiceTitleField(Priority.objects.all(),
# initial=Priority.objects.get(default=True).id,
# title_source_field='long_description')
#
class SelectWithTitle(Select):
'''
An overriden select widget to be used with ModelChoiceTitleField
'''
def render_options(self, choices, selected_choices):
def render_option(option_value, option_label, option_title):
option_value = force_unicode(option_value)
selected_html = (option_value in selected_choices) and u' selected="selected"' or ''
return u'' % (
escape(option_value), selected_html, option_title,
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, option_title in chain(self.choices, choices):
if isinstance(option_label, (list, tuple)):
output.append(u'')
else:
output.append(render_option(option_value, option_label, option_title))
return u'\n'.join(output)
class ModelChoiceTitleIterator(ModelChoiceIterator):
'''
A ModelChoiceIterator that includes the label for the options
'''
def __iter__(self):
if self.field.empty_label is not None:
yield (u"", self.field.empty_label, "")
if self.field.cache_choices:
if self.field.choice_cache is None:
self.field.choice_cache = [
self.choice(obj) for obj in self.queryset.all()
]
for choice in self.field.choice_cache:
yield choice
else:
for obj in self.queryset.all():
yield self.choice(obj)
def choice(self, obj):
'''
obj is each queryset object instance (i.e. each row from the DB)
'''
if self.field.to_field_name:
key = obj.serializable_value(self.field.to_field_name)
else:
key = obj.pk
return (key, self.field.label_from_instance(obj), obj.__dict__[self.field.title_source_field])
class ModelChoiceTitleField(ModelChoiceField):
"""
A ModelChoiceField that also provides an iterator with labels for options
title_source_field - the field name that will be used to retrieve the title for the element
"""
widget=SelectWithTitle
def __init__(self, queryset, empty_label=u"---------", cache_choices=False,
required=True, widget=None, label=None, initial=None,
help_text=None, to_field_name=None, title_source_field=None, *args, **kwargs):
super(ModelChoiceTitleField, self).__init__(queryset, empty_label,
cache_choices, required, widget, label, initial, help_text,
*args, **kwargs)
self.title_source_field = title_source_field
def _get_choices(self):
# If self._choices is set, then somebody must have manually set
# the property self.choices. In this case, just return self._choices.
if hasattr(self, '_choices'):
return self._choices
# Otherwise, execute the QuerySet in self.queryset to determine the
# choices dynamically. Return a fresh QuerySetIterator that has not been
# consumed. Note that we're instantiating a new QuerySetIterator *each*
# time _get_choices() is called (and, thus, each time self.choices is
# accessed) so that we can ensure the QuerySet has not been consumed. This
# construct might look complicated but it allows for lazy evaluation of
# the queryset.
return ModelChoiceTitleIterator(self)
choices = property(_get_choices, ChoiceField._set_choices)