1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93 | #
# 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)
|
Comments
Awesome, thanks :P
#