# # 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'' % escape(force_unicode(option_value))) for option in option_label: output.append(render_option(*option)) 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)