- Author:
- facundo_olano
- Posted:
- March 2, 2011
- Language:
- Python
- Version:
- 1.2
- Score:
- 0 (after 0 ratings)
This class makes easier the job of rendering lists of model instances in django templates. It's intended to mimic the behavior of the Model Forms in that it contains the code needed to render it as an HTML table and makes it easy to handle all the model lists from a single view (as it's usually done with the generic views for creating and updating model instances).
It also supports pagination and provides hooks for subclassing and customizing the rendered fields, column titles and list order.
Basic example:
class Account(Model):
name = models.CharField(max_length=MAX_LENGTH)
responsible = models.CharField(max_length=MAX_LENGTH)
email = models.EmailField()
class AccountModelList(ModelList):
class Meta:
model = Account
fields = ['name', 'responsible'] #email won't get a column
The model list would be instantiated with something like:
model_list = AccountModelList(instances=account_queryset)
Then a table header can be rendered with model_list.as_table_header(), while the table rows can be rendered calling as_table() on each model_list.items element.
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
|
More like this
- Template tag - list punctuation for a list of items by shapiromatron 10 months, 3 weeks ago
- JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 11 months ago
- Serializer factory with Django Rest Framework by julio 1 year, 5 months ago
- Image compression before saving the new model / work with JPG, PNG by Schleidens 1 year, 6 months ago
- Help text hyperlinks by sa2812 1 year, 7 months ago
Comments
Please login first before commenting.