#
# 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'<option value="%s"%s title="%s">%s</option>' % (
                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'<optgroup label="%s">' % escape(force_unicode(option_value)))
                for option in option_label:
                    output.append(render_option(*option))
                output.append(u'</optgroup>')
            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)