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
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193 | from django.forms.models import fields_for_model
from django.core.paginator import Paginator, EmptyPage, InvalidPage
from django.utils.datastructures import SortedDict
class ModelListItem(object):
""" Item for a ModelList. It represent a model instance. """
def __init__(self, instance, fields = None, exclude=None):
self.instance = instance
self.fields = fields
self.exclude = exclude
def as_table_row(self):
"""
Returns the list item as a table row, where every field
is a table cell.
"""
row = u""
for field in self.as_field_list():
row += u"<td>" + field + u"</td>"
return row
def as_field_list(self):
""" Returns the list item as a list of pretty-printed values. """
field_list = []
for attr in fields_for_model(self.instance, self.fields, self.exclude).iterkeys():
field_list.append(self.get_value(attr))
return field_list
def get_value(self, attr):
"""
Returns a string representation of the value of the given field name.
First tries to use a function get_<attr>_value, otherwise infers the
value.
Subclasses of this can define that function for specific display.
"""
function_name = "get_" + attr + "_value"
if hasattr(self, function_name):
return getattr(self, function_name)(attr)
#for choices
choice_display = "get_" + attr + "_display"
if hasattr(self.instance, choice_display):
return getattr(self.instance, choice_display)()
value = getattr(self.instance, attr)
if value is True:
return u"Yes"
elif value is False:
return u"No"
if value is None:
return u""
return unicode(value)
class ModelList(object):
'''
An object representing a list of models to use in template rendering.
It's analogous to the Django ModelForm.
'''
def __init__(self, instances=None, paginate_by=None, page=1,
order_by=None):
self.__complete_meta__()
self.__override_parameters__(instances, paginate_by, order_by)
self.items = [self.__new_item__(i) for i in self.Meta.instances]
if self.Meta.order_by:
self.__order__()
if self.Meta.paginate_by:
self.__paginate__(page)
def __new_item__(self, instance):
"""
Given an instance to be added to the model list, a new model list item
is created. Useful for overriding in subclasses to construct complex
list items.
"""
return self.Meta.item_class(instance, self.Meta.fields,
self.Meta.exclude)
def __complete_meta__(self):
"""
This method ensures that the Meta class has all the needed attributes,
so only those to that change have to be overriden.
"""
for field in dir(ModelList.Meta):
if not hasattr(self.Meta, field):
setattr(self.Meta, field, getattr(ModelList.Meta, field))
def __override_parameters__(self, instances, paginate_by, order_by):
if instances is not None:
self.Meta.instances = instances
if paginate_by is not None:
self.Meta.paginate_by = paginate_by
if order_by is not None:
self.Meta.order_by = order_by
def __paginate__(self, page):
"""
Filters the queryset and returns the correct page object
"""
paginator = Paginator(self.items, self.Meta.paginate_by)
try:
self.page_obj = paginator.page(page)
except (EmptyPage, InvalidPage):
self.page_obj = paginator.page(paginator.num_pages)
self.items = self.page_obj.object_list
def __order__(self):
reverse = False
if self.Meta.order_by.startswith('-'):
reverse = True
self.Meta.order_by = self.Meta.order_by[1:]
try:
self.items.sort(key=self.__key_function__, reverse=reverse)
except AttributeError:
#if the order_by is not a valid field, don't order
pass
def __key_function__(self, item):
value = item.get_value(self.Meta.order_by)
if value.startswith(u'$'):
value = value[1:]
if value.endswith(u'%'):
value = value[:len(value) - 1]
try:
return float(value)
except ValueError:
return value.lower()
def as_table_header(self):
field_names = self.field_names()
tds = [u'<td>' + field_names[field] + u'</td>' for field
in field_names.keys()]
return u''.join(tds)
def field_names(self):
model_fields = fields_for_model(self.Meta.model,
self.Meta.fields, self.Meta.exclude)
return SortedDict((field, self.get_name(field)) for field in model_fields)
def get_name(self, field):
"""
Returns the name of the given field. First tries to use a function
get_<field>_name, otherwise it uses the field label.
"""
function_name = "get_" + field + "_name"
if hasattr(self, function_name):
return getattr(self, function_name)(field)
return field.capitalize()
class Meta:
model = None
instances = []
fields = None
exclude = None
item_class = ModelListItem
paginate_by = None
order_by = None
|
Comments